Well, everything described in Part I works perfectly fine when your VNETs are using Azure provided DNS Server.
But if you VNETs are using customs DNS servers, as in my case, things get a little tricky. You use custom DNS servers because you are living in a hybrid cloud solution and resources from Azure must also find resources from on premises data centers adn in this case, standard Azure provided DNS servers are of no help.
In this case, when resources are trying to resolve name for your web apps with private integration activated, no DNS server will know about them. And in this case, there at least two options available:
A simpler approach (tested and working) is to add the IP of the Azure resolver (220.127.116.11) in your list of custom DNS servers for the VNET. Don’t add it on the first position, as this might bring trouble in resolving on premises resources and also not on the third position or lower because this will increase the time needed for name resolution and potentially can throw timeout errors. Adding it on the second position looks like a reasonable compromise.
As all Azure web applications are directly available over Internet, hence public, most of the times I need to have some form of protection for them, like an Web Application Firewall. And in Azure, I have at least two options for that:
Using an Application Gateway with WAF in front of an Azure Web App
Using Azure Front Door with WAF in front of an Azure Web App
Both options suffer from the same basic problem. The web application is still public and can be directly accessed using it’s URL (appname. azurewebsites.net).
I know that I can restrict access to the web apps by IP addresses but in a complex setup, with one web calling another and so on, I would like first to have inter web apps traffic not going over internet and second, a more elegant solution to restrict access to the web apps themselves, something like as in the diagram below:
In preview, Azure now has the “Private endpoint connection” functionality, which allows the creation of a private endpoint for a web application. This means that once created, the web application is no longer accessible from Internet but only from Azure networking resources (VNET, subnets, and so on).
Also, traffic from the web app is directed over Azure Private Link, the private endpoint being assigned an IP address from the VNET to which the web app is integrated to and so there no traffic over Internet. More than this, if you have an Express Route or VPN connected to your on premises resources (such as a database) then traffic between Azure web application and on premises resources will go trough Private Link and Express Route or VPN. Advantages:
The web app can be secured, by eliminating public exposure
Secure traffic flow between web application and on premises resources, over VPN or Express Route
Avoid data exfiltration from VNET
Private Endpoint created is used only for the incoming traffic to your web application. Outgoing traffic will not use this Private Endpoint, but the VNET integration feature.
Note: The VNet integration feature cannot use the same subnet as Private Endpoint, this is a limitation of the VNet integration feature.
Ok, I can secure the web application by not allowing any public traffic to it, but I still want it to be Internet accessible and protected by WAF. In this scenario, you simply put an external facing Application Gateway in front of the web application, as in the diagram above and the web application can accept traffic from Internet, traffic which is filtered by Application Gateway WAF.
If the web app needs to be accessible from other Azure VNETs or on premises networks then, instead of a public facing Application Gateway, you can put an internal facing one, with the same results (web app can also be accessed internally, from other VNETs using VNET peering).
This Private Endpoint feature I find especially useful when I have more than one web app, which are called one by the other. Instead of setting IP restrictions to each web app (and making sure that IP from the calling web app is whitelisted by the called one), I can integrate them all with private endpoint, so making sure that traffic from one to another is allowed (because they are usually on the same VNET) and having all public traffic denied. And in front of the entry point web app I can put an Application Gateway with WAF to be able to securely access it from Internet.
Note: Using an Azure Front Door instead of a public facing Application Gateway will not do the trick. Web app will still reject traffic coming from Front Door.
Setting up Azure Private Endpoint integration is quite simple:
Assuming there is already a VNET and the corresponding subnets created, first step is to integrate the web application with a VNET and a subnet.
As highlighted above, you will need a different subnet for the Private Endpoint than the one you have integrated the web app with.
Create the private endpoint and from that moment on, incoming traffic will be restricted to only Azure as source traffic. Incoming traffic will go trough the Private Endpoint and outgoing traffic will go trough the VNET integration subnet.
Once cretaed the Private Endpoint, DNS provided by Azure will cease to work and a Private DNS Zone will have to be created. Microsoft has details on it, here. Basically, a private DNS zone with the name of privatelink.azurewebsites.net will have to be created and registered with the VNET in which the Private Endpoint has been created
Once created the Private Endpoint, then you can proceed with an Application Gateway creation and web app will be secured.
Running less software sometimes is more when you consume it as a service.
“Run Less Software: If a component has become a commodity, we shouldn’t be spending precious development time on maintaining it, instead we should be consuming it as a service. In the history of enterprises this is controversial, but even containers are now run and operated as a service. If your engineers aren’t building data centers any more, why are they building container platforms? “
In Part I, I’ve discussed how I’ve analysed the applications landscape and what criteria were used to prioritise the modernisation efforts.
Upgrade to a Windows OS version with LTSC
Upgrade to a .NET Framework or .NET Core version with LTS
Classify you applications according to systems of a record, systems of differentiation and systems of innovation for a better prioritisation
Investigate options for reducing operational overhead (move to PaaS or SaaS solution, use containers, use DevOps)
Make an in depth application assessment looking for:
Source code availability
3rd party components
Running versions of Windows OD, .NET, .NET Core
Type of application (desktop, web, Windows Service, IIS Service)
Technologies incompatible with moving the app into cloud
Embedded config parameters
Hostname, DNS dependency, localhost dependency, etc
Rights for the application to be able to run properly
Application security (authentication and authorisation)
If you want to port the application, make a list of technologies that are used by the app and are not compatible with .NET Core and seek for alternatives
Windows Communication Foundation WCF
Windows Workflow WF
ASP.NET Web Forms
Check the use of entity framework
To further evaluate the applications, I’ve used a set of Microsoft tools that can provide an evaluation of the current application state. They are available as extensions to Visual Studio or standalone tools.
.NET Portability Analyser – is a tool that analyses assemblies and provides a detailed report on .NET APIs that are missing for the applications or libraries to be portable on your specified targeted .NET platforms. The Portability Analyser is offered as a Visual Studio Extension, which analyses one assembly per project.
.NET API Analyser – The .NET API Analyser is a Roslyn analyser that discovers potential compatibility risks for C# APIs on different platforms and detects calls to deprecated APIs and comes as a NuGet package.
.NET Framework Analyser – You can use the .NET Framework Analyser to find potential issues in your .NET Framework-based application code. This analyser finds potential issues and suggests fixes to them and also can highlight any issues that need to be addressed prior to moving to a new version of .NET Framework or .NET Core.
2. Select a modernisation approach
The approach to modernisation and the prioritisation of investment (development effort) have to be dictated by business priorities (business strategy) and requirements. Also, data gathered from the in depth assessment of the application have to be taken into account when establishing a modernisation approach.
In my landscape, the architectural drivers for modernisation were:
Improved stability – critical applications were having a high fault rate and the maintenance effort was pretty high due to deprecated technologies
New functionalities – had requests from business for new functionalities, which were cheaper (effort wise) to implement if we first modernised the app and moved it to cloud
Ability to respond to customer faster – had requests from customers (trough business demands) for new functionalities that couldn’t be provided in a short time frame, short enough to become a market advantage
Quicker bug fixes – as we own majority of the source code for our landscape, that wasn’t a very important driver but it can be added to the list
Improving scaling capabilities – this was an important driver for us, as N-tier applications are quite difficult to scale horizontally (taking advantage of the cloud elasticity capabilities) and vertical scaling has it’s limits (both technically and financially)
New market challenges – again, N-tier based applications are not very agile and keeping up with services offered by competitors is hard to do, especially in a dynamic sector like banking and finance
From an operational overhead point of view, following things had to be improved by modernisation:
Applications with obsolete functionalities, requiring large amount of support work – I’m having in the landscape old applications, which now are used only at a fraction of their functionalities but still critical therefore requiring high SLAs and dedicated support staff
Skills – well, not every average .NET developer we can hire still remembers .NET Remoting and how to work with it
Internal standards and IT strategy – as we are trying to move in an Agile direction (this implies also a change in our technical thinking, more APIs, REST services, containers, DevOps approach) obsolete technologies are becoming more than a technical debt, more like a barrier. Try to run a .NET Remoting app in a container or try to horizontally scale it
Cloud migration options for applications
According to industry specific guidelines (Gartner and others) we have the following options when migrating applications to cloud:
Lift-and-shift aka Rehosting
Refactor or Rebuild
Each of these methods has its trade-offs. Modernisation efforts fits into every one of the above mentioned approaches (except lift-and-shift, which is not a modernisation per se) as my long term goal is to move most of the applications to the cloud.
Lift-and-shift – It means that basically we have taken or apps from on premises data center VMs to cloud VMs (ok, plus some additional networking to support the infrastructure). This was the first wave of cloud migration in my landscape and it worked pretty well for the intended purpose which was to lower the operational costs and maintenance overhead. Also we have gained some increased uptime (it’s nice to have a VM Scale Set configured in a couple of minutes instead of hours or days) but that was not modernisation, merely tinkering around the edges of the application rather than make significant changes.
So, whenever you want to reduce the operational and support overhead and (or) cannot make significant changes at application code level (maybe you don’t have the source code anymore, maybe the app is making extensive use of obsolete technologies, maybe the cost vs benefits ratio is not making the business case) then lift-and-shift approach will do just fine.
Revise – In the first step, we tried to get away with minimal changes to the applications, goal being able to move them to PaaS cloud solutions (Azure Web Apps) and on the long term to use Windows Containers. What we had done for this was just to update .NET Framework to an LTS version and solve the eventual incompatibilities and also solve the no-go cloud technologies that were used by the app (hard coded config parameters, local file system usage).
Replatform – It’s kind of a middle ground between lift and shift and rearchitect of refactor. It involves a bit of modifications to the code to take advantage of the new cloud infrastructure. For example, for a part of my landscape I’ve decided to replatform, I’ve did some changes, like:
Instead of internal message queues we switched to Azure Service Bus
No more local file system storage allowed, switched everything to storage accounts
Use of Azure Web Apps deployment slots for test/dev/staging environments
In this change, we achieved some cost reductions (compute density for Web Apps it is anyway better than for individual VMs), better release management, increased SLA and lower operational overhead (no internal effort needed for Web Apps) and also laid the ground for the next wave of modernisation.
Rearchitect – It involves a complete rearchitecting of the app to better suit the cloud environment (Azure in my case). This involves significant alterations to the app. It has also the advantage that we can target specific cloud services (like AKS or AWS Elastic Beanstalk or AWS ECS). We are in process of doing just that with some of the most revenue intensive applications, using a microservices approach.
Rebuild – Basically, you will rewrite the app from scratch, trowing away the existing code base. In this case, first ensure there is a valid business case for it and support (and funding) from the stakeholders. I have some candidates for this kind of work, but the business case is not justifying it.
When choosing a modernisation approach, evaluate also if you can replace it with SaaS solution. If the app is supporting business processes that are making little value or they are not a differentiation (think of HR, facility management and so on) then it makes a lot of sense to just replace it with a SaaS offering.
Another viable option is to just retire an application. If the business process has been updated and no longer requires it or of the business is not using it anymore (or using just 10% out of it, maybe a report or two but you still support it) then it’s a good idea to just retire it. To quote Gregor Hohpe “If you never kill anything, you will live among zombies“.
Choose an Architectural Approach
As I advanced further with application assessment, it become clear that I should choose at lest one architectural approach for the modernisation efforts (actually, I’ve chosen two approaches, depending of the business value of the applications). I’ve had a list of constraints like:
Increase delivery agility
Increase applications capability to further innovate and sustain change
Lower running costs
So I was beginning to look into APIs and microservices and I came up with a concept inspired by Gartner’s MASA proposal.
MASA stands for Mesh Apps and Services Architecture and is an agile architecture composed of decoupled apps, mediated APIs and services. It includes architectural principles like decoupling apps components using APIs, create services of optimal granularity (see Domain Driven Design) and it advocates designing fit-for-purpose apps. Each component has an API (or at least will consume an API), and when all connected, they will form a mesh of interconnected services and applications.
Using this approach, allowed us to simplify somehow the apps, to use .NET Framework or .NET Core in various apps and also enabled us to use polyglot persistence (with SQL and NoSQL).
We sliced the applications in kind of microservices. And I say kind of microservices because we didn’t followed exactly all the guidelines to segregate the apps down to the lowest level but actually to what was comfortable for the development teams and from requirements perspective. And we end up having macroservices.
A good reason for this is that we are starting from monolithic applications, some of them being quite big (think of a core banking app). In this case is not practically to go directly to by the book microservices, instead we are developing new functionalities as micro or macro services and in the same time we are extracting functionalities and redevelop them as standalone services (see strangler design pattern) .
Mediated APIs apply one or more mediators to manage communication between an API consumer and the service that implements the API. API mediation reduces the complexity of managing multiple back-end services and increases the choice of technologies and models used to build services and in this scope, another core component is an API Gateway.
What I can say is that going to a macroservices (or microservices) way is not an easy thing. We had long internal discussion about setting the scope border for each service, sometimes we got back to the drawing board, realising that initial scope is wrong (too broad, or too narrow). Also, when applying a strangler pattern and started to slice a monolith, data persistence problems and databases consistency along the old monolith and the service which is sharing most of the same persistence repository are hard to deal with.
As we started this process of modernisation about 9 months ago, we are now in full traction on it. The initial assessment and in depth analyses did take about 3 months and since then we are in full development phase. AAs we started with some low hanging fruits, now we already see some benefits.
Costs for running applications that have been modernised and moved to Azure (with full shebang on cloud functionalities) is definitively lower than before. Also support and maintenance overhead is lower and horizontal scaling works like a charm when things are done properly.
We have improved our SLAs and decreased the incidents, meaning we have now a happier business with willing to going further investing in this modernisation.
We also had many obstacles. For the first, is start to decide from where to start because in the previous landscape. monolithic apps were so tightly coupled that any change would brake a lot of things.
Also, when modernising an app that depends of other apps, a lot of interim solutions must be provided, until we could modernise also the other apps.
We had to implement an ODS (Operational Data Store) just for decoupling apps, in the first stage of the modernisation process (ODS was a good idea, we will keep it for good).
Overall, things are looking good, investment was worth making it and it started to pay off.
As an architect in a company that is basically a “Microsoft shop”, most of my landscape (if not all) is made out of custom applications based on Microsoft technologies and .NET Framework, with versions starting from .NET 2.0 up to .NET 4.5, which are running along enterprise Microsoft applications like Sharepoint, Dynamics, Office 365 and so on.
The custom applications landscape has both desktop applications, built on a heavy client philosophy, making use of .NET Remoting functionalities (which are long deprecated) and more modern web applications, built using .NET 4.0.
As we are moving the landscape to Microsoft Azure cloud, I’m facing some challenges about bringing old applications to current century, making them ready for a cloud migration, deciding what will be migrated as lift and shift, where we will use replatform and what applications will be refactored.
In any of the above mentioned migration strategies, a major show stopper is , in many cases, the legacy technologies used by applications. Old versions of .NET Framework or obsolete technologies that are preventing us to even try a lift and shift approach must be dealt somehow and a long term approach to modernise and further support our business applications must be established.
Microsoft .NET Framework was released in 2002 and since then the company have invested heavily in the entire landscape up to the point where we have about 70% of the business applications portfolio based on .NET.
In 2019, Microsoft announced that its development effort will be focused on .NET Core, with the Windows .NET Framework moving to LTS (Long Term Support). The consequences are that only bug fixes and security patches will be provided and no new features will be added to .NET Framework.
All of our applications are still meeting their business requirements and business is heavily relying on them to perform and bring revenue. On the same time, we are trying to accelerate application delivery and move to cloud-native architectures for the new applications we are providing for the business and also to decide how old applications are going to be fitted in the cloud landscape. Also we intend to move away from the traditional n-tier application design and use modern architecture patterns like APIs, microservices and so on.
From a technical perspective, there are some standardised steps to be taken when reviewing the applications landscape.
Identify end of life platforms and .NET Framework versions for each application
When talking about application support and compatibility with newer .NET versions, the following criteria I’ve found out to be of significant importance:
NET Framework and .NET Core version support
Windows OS end of life
.NET Framework and .NET Core version support
According to Microsoft, at the time of writing, their official support policy for .NET Framework I’ve summarised it in the table below:
After compiling the tables above, I’ve decided that applications which use a non LTS .NET Framework or .NET Core must be migrated to a LTS version. In some cases this is just a matter of recompile and redeploy the application (fortunate cases where the company is the owner of the source code). For others, where we have lost the knowledge about source code, requirements and so on, they are perfect candidates for assessment of application business value, consolidation of functionalities to another application or platform or just plain rewrite from scratch, if the business revenue justifies it.
The same applies to work in progress applications, where it is easier to switch to a LTS version of .NET or .NET Core because we were using anyway a newer version of .NET.
For the externally sourced applications, things are a bit more tricky because in some cases we have to wait for a new major version release from the vendor (if we are lucky enough that the vendor has something like this in his roadmap).
In other cases, the vendor may have a SaaS equivalent solution that might work for us (if all constraints allow the use of a SaaS solution instead of a hosted one).
If none of the above can be done, then it all comes to a business decision.
Windows OS versions support
Currently, the picture for Windows OS support looks like this:
End of life
Windows Server 2019
Yes, until January 2024 with Extended until January 2029 Available also as container image
Windows Server 2016
Yes, until January 2022 with Extended until January 2027 Available also as container image
Windows Server 2012 R2
Ended on October 2018. Extended until 2023
Windows Server 2012
Ended on October 2018. Extended until 2023
Windows Server 2008 R2
Ended on January 2015. Extended until January 2020
As you can see, Windows Server 2019 and 2016 are under support, with Long Term Servicing Channel, meaning we can rely on them for the next 5 to 8 years. Also, an important aspect is they are also available as Windows Container Images and we can take advantage of that trying to move some applications to Windows Containers, Doker and Kubernetes on Azure.
My end goal is to have most of the applications prepared to run in Azure cloud, in a hybrid environment. Of course, there were still be stuff on premises, stuff that can’t be moved to cloud due to regulations or just it is to expensive to refactor and run it in cloud.
Also, as we operate in many countries (Europe, South America and Africa), another goal is data center consolidation and for this to be efficiently accomplished, I have to have all applications modernised to be monitoring and automation friendly (Remember: Never send a human to do a machine’s job)
Now, after I’ve established a clear course of action regarding .NET platform and Windows OS versions, I had to come with a prioritised list of applications and a modernisation method for each of them.
The first step towards this is to identify which modernisation approach works best for each application, decision which is more than a technical one. First, you have to assess the business value for each application and then see which app will benefit the most from modernisation and also what kind of modernisation should be applied. Some high value, high revenue applications are worth investing in and makes sense to go on the microservices path but others will do just fine with a rehosting approach. Also, the effort for modernisation should be realistically taken into account and aligned with budgeting and projects portfolio (for the execution phase, TOGAF ADM might be a valuable tool).
In my case, for prioritising applications, I’ve used Gartner’s definition to create viewpoints by which the applications portfolio can be viewed, to identify potential modernisation candidates.
Gartner identifies three main application categories:
Systems of a record: capabilities that have a clear focus on standardisation or operational efficiency.
Systems of differentiation: business capabilities that enable unique processes or industry specific capabilities.
Systems of innovation: new business capabilities to address emerging business needs, new business opportunities and modes.
Whatever applications fall under systems of differentiation or systems of innovation are sure candidates for modernisation effort.
Actually, here the discussion is a bit broader, as many applications from those two categories are tightly coupled with some system of records, which in turn must be somehow touched to sustain the modernisation effort (for example, if a system of differentiation is a mobile banking application which is tightly coupled with CRM, which is a system of a record and CRM is an old monolithic application, for implementing, let’s say, real time push of data from CRM to mobile banking then CRM must be modified or another layer added in between).
Another goal of modernisation was to reduce the operational overhead associated with applications support and here there a couple of options:
Move to a PaaS solution, switching the overhead to a service provider
Migrate to a better cloud integrated platform like Azure Kubernetes Service – AKS and use all available functionalities for support, monitoring and maintenance
Long shot – adopt a DevOps approach. This wasn’t taken into account in the first phase of the modernisation as the organisation was not ready
For assessing each application current state and actions and effort estimation for modernisation, I’ve used a list of steps, like below.
1. In depth application assessment
Review the current state of the application
Do we have the latest version source code available? If yes, is this source checked in a version control tool? Is available and documented? Is there some knowledge about it in dev teams?
Is the source code functional? Can be built a running version of the application?
Do we know all 3rd party components and libraries used? Do we have them. in a build ready state?
Are the 3rd party components available in an LTS .NET Framework version? Are they available for .NET Core? Are they still maintained and supported?
Is the application using .NET Framework or .NET Core? And what version?
On what version of Windows is currently running? Does the version of Windows match our LTS requirements?
What kind o application are we assessing? Is a heavy client desktop application, a web app, a Windows Service or an IIS service?
Assess the use of technologies that are not compatible with a move to Azure cloud.
Legacy applications often used technologies that were appropriated for on premises intranet hosting but if you try to move them to cloud, those approaches are not working anymore. Issues that I’ve faced are listed below:
Application is storing files, either temporary or long term on a local files system, on path hard coded or configured
– Remove absolute paths and migrate to block storage (Azure storage accounts)
Application uses non standard logging system and / or writes logs locally
– Use a centralised logging system – Use standard .NET logging components – Use Azure Application Insights as much as possible
Embedded configuration parameters
Configuration parameters are stored in config files or hard coded in the application
– Use a centralised config repository and modify the app to extract config parameters from the repository – Make use of Azure Key Vault for securely storage of config parameters
For web applications, state is not explicitly managed or is managed in databases
State management for web application is not dealt with, leaving web servers to handle it or is stored in databases
– If you want to take advantage of cloud horizontal scaling and elasticity capabilities, state must be taken out of the application itself and be stored and managed separately. – Make use of Azure Redis cache or other in memory caching tools for state management
Application is using SQL databases
In some cases, if the application is not utterly complex or not heavily transactional, then it can make sense to try and change the database from an SQL based one to a NoSQL DB, like Cosmos DB for cloud hosting. A proper designed NoSQL data model can simplify the data access layer and also can come with a cost reduction in cloud hosting. Instead of having an SQL instance to pay for, you just pay for Cosmos DB capacity. Of course, in such a shift in DB technology there are also other things to consider (if app really needs a NoSQL DB, dev teams skills, cost estimation and so on)
Sending one to many messages on a network segment. Haven’t met the case yet.
Change to message queues
Localhost IP addresses
Application is using localhost or 127.0.0.1 addresses
Ensure that application can lookup it’s own hostname
Hostnames or DNS dependencies
Hard coded addresses or URLs areused
Same as for config parameters, use a centralised repository or service discovery
Full trust code or admin user
Application requires elevated privileges to run
Identify what and why there such a need and change it
Application is using a built in log in mechanism
Make use on single sign on. If moving to cloud is an option, then use Azure AD, or Azure B2C AD
.NET Standard is a formal specification of .NET APIs that are intended to be available on all .NET implementations and are included in modern .NET implementations as part of the Base Class Library (BCL).
NET Standard versions are of two types:
Additive: .NET Standard versions are logically concentric circles: higher versions incorporate all APIs from previous versions.
Immutable: Once shipped, .NET Standard versions are frozen
I advise to target the latest LTS version of .NET core and also the latest LTS version of .NET Framework (4.8)
If we decide to rewrite the application to .NET Core, let’s see what components are not compatible with .NET Core and will never be added to .NET Core
Windows Communication Foundation WCF: This a framework for building service-oriented applications. Using WCF, you can send data as asynchronous messages from one service endpoint to another. A service endpoint can be part of a continuously available service hosted by IIS, or it can be a service hosted in an application
ASP.NET Web Forms
.NET Remoting: .NET Remoting is a framework where you can invoke or consume methods or objects in a remote computer named the server from your computer, the client. .NET Remoting was superseded by WCF in later versions of .NET Framework, and only remains a component of .NET Framework for backward compatibility purposes
Windows Workflow WF: Technology that provides an API, an in-process workflow engine, and a rehostable designer to implement long-running processes as workflows within .NET applications
If you are using one of the above technologies, then alternatives must be searched for or a major rewrite of the application.
Check to see if any kind of entity framework is used
Official Definition: “Entity Framework is an object-relational mapper (O/RM) that enables .NET developers to work with a database using .NET objects. It eliminates the need for most of the data-access code that developers usually need to write.”
If you are not already using it, I strongly suggest using entity framework (or other O/RMs) because it helps saving a lot of time in the development process and also helps in standardising data access and manipulation for your applications.
As per the above figure, Entity Framework fits between the business entities (domain classes) and the database. It saves data stored in the properties of business entities and also retrieves data from the database and converts it to business entities objects automatically.
Also Entity Framework is available for .NET Core, as per below table.