 My name is Jason Webber. I'm a Principal Lead Consultant with Magenik. And this session came about from my experience and the experience of my colleagues performing a number of migrations into the cloud, Cloud Foundry, and other paths or platform as a service solutions. We have our obligatory fire announcement. I suspect you've already seen this a few times today, so I won't read it for beta. But look around, find the exits. The exits get out. From out, you can get to outside. And of course, I have every confidence that everyone here is going to be nice and polite to the public safety folks in the event of a fire. So that's interesting. Whatever happens, don't be that guy or gal. OK. So there's no necessary reason that a legacy migration of a .NET framework app, as big as it may be, needs to happen as a big bang, a giant exercise all at once, after Cloud Foundry lands. That's an OK way to do it, if that's what you want. But it's not the only way. There are things you can do before you have access to the platform. You can do these things in place in your running application, keep the lights on, keep the application going, and improve the quality of that application while at the same time making progress towards your eventual migration or potentially your replatforming goals. And there are some benefits to this approach. So you get a head start on uncovering all the things you didn't know. You're going to learn about the application. You're going to learn about the process. That's going to benefit the people involved. And it's also a good risk mitigation strategy. The sooner you know about a challenge, the sooner you can begin to address it, factor into your planning. And frankly, you'll get better estimates the sooner you start. So we're going to cover that. But we're not going to cover. So today, we're going to go over in the next 20 minutes roughly a framework for thinking about migration. This is broad sense. We'll cover a pre-flight checklist, some things we should go through prior to actually beginning one of these endeavors. We have the five ways of improving things and making progress on your migration goals. And then we'll wrap up. And there will be code, as promised. So before we start, given the audience, I want to make sure that some of the terms I'm using are going to be clear. So lots of these, I'm sure you've already heard today and perhaps before the session, before this conference. Migration, lift and shift, fork lift migrations, cloud ready. For our purposes, these all mean essentially the same thing. You're going to take an application and with roughly speaking minimal changes, just pick it up and move it into Cloud Foundry. That's one set of goals. That's what we're going to be talking about today, how to get closer to that. The next two are different in important ways. So cloud native or replatforming, these are typically much more significant efforts. If you're starting with a legacy.net framework application, a big one, to get there to the point where you're taking full advantage of the Cloud Foundry platform, lots more effort. Architectural refactorings, all kinds of things going on. That is not what we're going to be talking about today. Although the good news is, the progress you make towards that first goal, that smaller goal, can help you in some cases meet the larger goal as well. So no loss. And then lastly, I'll be distinguishing today between barriers to scale, meaning these are things that are going to inhibit your application from scaling out versus barriers to migration. And these are things that we have to tackle, must do's, if you will, in order to actually migrate a legacy application. Now by show of hand, I'm curious, how many folks in the audience today have heard of the so-called 12-factor app or 12-factor application? All right. That's good. So for those of you who haven't, this is a way of thinking about software. You could say it's an architectural, a set of architectural patterns for developing code that runs in Cloud Foundry. You could also say it's a way of thinking about an existing application and measuring its suitability to run in the Cloud and Cloud Foundry. So we don't have time to go through all 12 of these today, and that's okay, because it turns out that for our purposes, to migrate a legacy application, some of these are more important than others. And the purists may push back on this and that's fine, we can have a conversation afterwards if you're interested. But we're gonna focus on these three today. Dependencies, configuration, and logging. These three must be addressed in some fashion. Historically, must be addressed in order to migrate an application, a legacy dot in that application, a framework app into Cloud Foundry. There's a little bit of an asterisk here because in late March, as you may be aware, Pivotal released PCF 2.1, and that release actually relaxes some of the constraints on what you can do, what you need to do in essence, to take that legacy application, that dot in that framework app, and run it in the Cloud. Now this is still important to be aware of though, because in many respects, if you don't do these things, you're not gonna be able to take full advantage of that migration. So it's not like you don't need to do these things, but technically some of these aren't really must-dos any longer. While we're doing this, we're gonna keep our eye on three other of the 12 factors. Now these are a little bit different. Based on my experience, these are the three factors that often introduce barriers to scale. So we don't actually need to solve for this in order to achieve a migration, but we're gonna keep our eye on these as we go. At least that's what I'd recommend. And there may be some low-hanging fruit here that you can pluck along the way. So these are nice to have. So out of 12, that gets us our framework, a three plus three framework, if you will. And this is gonna be the theoretical basis that'll guide our efforts. One of the ways to prevent bad outcomes in life in many endeavors, not just migrations, is to plan and to do a little bit of analysis before you start. Some people may take that as anti-agile, but really if you read carefully, the agile folks would say you do the right amount of planning and no more, which I agree with completely. By the way, this is autopolo and it turns out this has a history in Massachusetts. So kudos to the local crowd. So here's a pre-flight checklist. If this is something that you're interested in, if you have a big application, a legacy framework application that you're interested in migrating, I'd recommend these five steps first. So the first thing you wanna do is find a technical SME. Someone who knows the application really well, ideally, you do the best you can. If this person knows the business as well, that's even better. But for our purposes, for technical migration, that's actually secondary. If we can find a person like this, that person's not always available. If you don't have that person, this exercise is gonna become a giant archeology expedition, the code equivalent thereof. But if you do have this person, they can help us with the next four steps. And that is coming to grips with our dependencies, external dependencies, system dependencies, as well as shared state. So singletons are one example. Other shared state could be, you name it. In most legacy applications, there is a lot of shared state. As opposed to most modern applications, there's very, well, depends. You may discover there is a little or a lot of shared state depending on who wrote it and when. So dependencies and shared state. So having a large family with lots of dependents can bring a person much joy in life. But the same is not true in software. Software with lots of dependencies is painful. In migrating that software is hard because if you're not aware of these dependencies, they can really trip you up. So before we actually engage in migration activities, I strongly recommend that we put together a list of all the dependencies for a particular application. And these are examples of some of the things we're talking about. So again, PCF 2.1 makes this a little bit easier on us, but we should still do this for numerous reasons that'll become clear a little bit later on. So using the registry, using the global assembly cache, file system usage, session state, all these things you wanna be aware of. Because we may or may not be able to use them as is and these sorts of things may need some changing before we're able to really succeed in migrating to the cloud. Shared state is another one of these challenges for us. So everything is easy shared state-wise when you're a single process running on a single box. But one of the reasons we move to the cloud is to be able to scale out and shared state is the bane of scaling out. So just as with our dependencies, you'll wanna create a list of all the different forms of shared state. Could be physical shared state, meaning using a cache, I'm using a database, which is connected and that's okay. Could be logical shared state. Could be singletons in the code. That was a pretty popular pattern in the 90s. So depending on when your application was written, this could be very significant. And I have to say, I put this as number one because I actually think this is one of the most important things you can do from a practical standpoint in terms of really succeeding. If you're in an organization with a high tolerance for failure, you can skip it. And nobody likes this. Nobody enjoys doing this. But it's pretty important. So if we've done this, if we've gotten our list together, then we can do a little bit of a quick assessment and say, all right, are we ready to do this or not? So here's how we can assess whether we're ready or not. Do we have that SME? Or is this gonna be one of those slog should the code where we just don't know what we don't know and therefore we can't estimate how long this is gonna take? Does the SME agree with the rest of the team? Overall, in terms of how much effort this is, if there's a big mismatch, it's worth having a conversation about that. Some people are just those negative people and everything's always a disaster and they're gonna take a million years. Okay. But sometimes there's some valid concerns. You really wanna hear those folks out. They know a lot in many cases. How do we feel about our lists? Does the level of completeness of our lists match with our organizational tolerance for risk? Is it good enough? Do we understand our limitations? Have we figured out which parts we can succeed with and which parts we can't? Because in many organizations, you say it's a migration. Three conversations later, it's the CIO talking to someone and it's much more than a migration. So it helps to communicate very precisely. And then of course, are we comfortable with whatever benefits we expect to get having gone through this exercise? This is a good way to have that checkpoint and rationalize the effort. Great. So we've gone through that exercise. We think we're good to go. So the good news is you've already done one of the five things, the five ways. It was the creation of those lists and doing your due diligence. And if you feel cheated, I've got a number six at the end that will make that one up. So here's way number two that we can start. And again, this is in place in the current environment with the current application. Many legacy applications are running on older versions of .NET. And that's, generally speaking, not that great. If you're on 4.5.2 or later, you're more or less supported. Microsoft supports that version. But if you're running on 4.5.1 or earlier, technically you're out of support. So this is a great thing to do, whether you're migrating or not. Now, depending on where you're coming from, there can be subtle differences in the CLR versioning that can cause some problems. So this isn't a slam dunk. It's pretty easy to do. I'm not, I'm sure everyone in the room probably who has a technical background knows what's involved. So I'm not gonna go into details on this one. But it's worth noting that you can't assume it's gonna work. And so plan accordingly, maybe set up a VM, try to duplicate the environment, upgrade in place, see what happens. Test it out. Testing is probably the most important step here. And if you've got automated testing, that's fantastic. If not, you can maybe solicit help from other parts of the organization to test it out. Generally speaking, this is a can do or can't do. Occasionally there are some gotchas, but for the most part this is fairly straightforward. And you know reasonably quickly if it's gonna work for you or not. And if it's not going to work for you, this is a much bigger red flag from an organizational perspective. These earlier versions present security risks. And depending on what your application does could be a pretty big deal. The other thing I wanna mention in regard to versioning is that although it doesn't apply in the in place case that I'm discussing today, once you have your application and you're ready to migrate, as you start moving forward, once you get it into Cloud Foundry, odds are good if it's a .NET application that you'll wanna take a look at Steeltoe. So this is essentially a look ahead. Steeltoe is a fantastic implementation, re-implementation of Spring Boot. And even if you don't use the whole thing, there's lots of pieces that can be pulled out separately. It's hard to imagine you do a migration into Cloud Foundry and not be able to, given .NET, of course, and not be able to use at least one or two parts of this. And it's well written, it's all good. The reason I mentioned it here is that depending on when you'd go through this, you may discover that there's some version dependencies. So for example, back in December, Steeltoe supported 4.6.1, but not 4.7 yet. So it's just a timing thing. It does now. The new release of Steeltoe works with 4.7. So that's really just the timing thing. Here's way number three. We can do this in place. Externalize our configuration. And this actually bumps into some of the other, of the 12 factors, but the gist of this is that when you're running in the Cloud, you don't want to do what you may be doing on-premise. So how do you do configuration today? Well, lots of .NET apps have configuration files, app config, web config. And those, by and large, will continue to work, but it's not ideal. For a variety of reasons, when you're operating in a containerized environment, changing config via files, at least, is not what you want to do. So what we would like to do instead is figure out a way that we could inject our configuration via environment variables, which is a pattern you see in Cloud Foundry. And we want to do this. We want to enable this without breaking our application today because it's running in place on systems we need to keep the lights on. So what we're gonna do, what I propose, is you define a new interface, C-sharp, or whatever language you're using. You implement that interface using the current data source. You've already got the code. So whatever you're doing today, you just put that in the implementation. You've just added an interface. Then you can refactor your app logic to use that interface for the purpose of getting config. So instead of calling configuration manager, you're just calling your own method on that new interface that you've created. And I'll show this in a minute. Test it out. Then you can implement that interface and back it up with environment variables. And that will allow you to overlay the value of environment variables on top of your files. Or not use your files at all if you prefer. And then of course, test it. Always test it. It's like backups. Backups are easy. It's when you try to restore that you learn whether they work or not. So here's what that can look like in real code terms. This first block here is a demo key. This is a pretty conventional approach for getting a piece of information out of configuration using configuration manager from app settings or web.config. The second block here, I'm not sure if I can hit this with the pointer. Does that show up? Not really. So here what we're doing, this is the logic for how to pull a piece of configuration information out of the environment variables. And what you wanna do as we've showed here, you see the application config prefix. So the environment variables are a global namespace essentially. Who knows what other process is or anything else that's been installed on a particular host is gonna put in there. So you might have a name, just as you do in web config, you have a key name and then the value that you're pulling out. You're gonna wanna do that here, but what I'd recommend is that you establish some sort of unique prefix so that common names don't collide in that global namespace with other environment variables. An example would be path. Path is already set on every Windows machine. You can't use path. Use a prefix, path, you're good to go. So that's the code for that. Now in terms of making this essentially a pass-through, this is an implementation that does both of the things I just described. What we would do here is we would first pull the value out of the configuration file. I'm assuming that we're not gonna make any changes to those because that's not part of what we're trying to do here. And then we would overlay, as I mentioned, the value from the environment variable on top of that. So you can deploy as you have it today in the event that you get to the point where you can migrate to Cloud Foundry. You can literally take the set of assets you've got, push them in, the config file goes with them, and then you can add configuration at runtime via the environment variables and just overlay on top. Or you can just use environment variables. This is an example of the interface I mentioned. It's pretty straightforward. You don't need a lot. A key, a read method, and then the implementation, this is literally the implementation using the previous method, it's a one-liner. So the amount of, the actual amount of code change that's necessary to implement this is quite low. And this gives us the ability to then configure our application via the environment variables, which is something you're absolutely going to want to do in Cloud Foundry. This is something you can do today without turning the lights off. It's very straightforward. Way number four is to come to grips with logging in the cloud. So this is something that's gonna come up in any platform as a service environment, Cloud Foundry or otherwise. If you have an application running on a server, on-premise, you can remote in RDP if there's a problem, figure out what's going on, reboot it, restart it. And the cloud is a little bit different. So it becomes more important to have, for lack of a better way to put it, good logs. Logs that are going to give the information that's required by the different audiences that might use them. And I find it helpful when thinking about logging to keep in mind that sometimes some audiences use logs for things other than strictly speaking logging such as monitoring. So you may have operations folks who are interested in the current state of the system. You may have business analysts or business owners for a particular application who are interested in transactional stuff. How many transactions per minute are we running through the system? How many transactions are completing? How many shopping carts are getting left unbought, et cetera. And of course you have a development team and they're probably gonna be looking at the logs when there's a problem debugging, et cetera. So to address logging in the cloud, we can adopt a very similar approach to what we just did. And this actually sits on top of the configuration. So in Cloud Foundry, there's a logger gator it's called. And if you emit your logging information to standard out, it will pull it in and make it available through numerous mechanisms. Now a legacy application is probably writing it's log information into files on a database or maybe you're using a third party service. That's fine. You can keep doing that. Just as we injected a little bit of extra functionality to get configuration turned on, we can do the same thing with logging. These are the steps. We'll define a new interface, should mention. As a consultant, I've had the luck, good fortune to work on numerous applications in numerous environments. And one of the things I've learned doing this is that lots of different organizations have different ideas about how to do logging. I think there's a lot of different frameworks out there. So when we're aiming for Cloud Foundry, we're gonna wanna try and line up with standards. And the six log levels that I'll show you in a minute are based on the Microsoft standard. And they represent a framework to work with them and I think it's a good one. We might wanna follow that. So we're gonna implement an interface. We're gonna continue writing to whatever you're doing right now for logging, but we're also going to write the standard out. Then we'll refactor the app logic to use our new interface just as we do with config. Very similar. And of course we'll test as we go. Now in terms of getting logging working, we actually have some tools we can pull from. The Microsoft Extensions libraries are very easy to get via NuGet. These are the two you'll need to do this, at least to do the version I'm gonna show today. And in terms of the actual implementation, it's as simple. So the approach is different depending on whether you have for example, a web service, a web application or a stateful process, Windows service, you name it. But in general, the steps are the same. So in this example, we use the Microsoft Dependency Injection, the Microsoft Extensions to create a new service collection, which is a dependency injection class. And then you notice the next line is Configure Services. So this is taking a page from .NET Core actually. We'll write our own. This is a pattern, should be pretty familiar if you're familiar with .NET Core. Then we build the service provider and then we can use that service provider to get an instance of our logging interface. And this is our own Configure Services that we've written for this purpose. It's very straightforward. We're just gonna say, we're gonna use the extension method that the logging framework makes available to us. This is Microsoft's framework. We'll say add console and that'll get us our standard out. When we log something, it'll automatically be put to standard out. And we can also set a minimum logging level. We may or may not want trace turned on most of the time. Typically, you would not do this. You would not run your trace logs all the time. You could make this configurable as well. You might choose to drive your logging level based on an external variable that you can change in the environment, which is something that SteelTow can do for you. So here's an example of the Microsoft's logging interface, just a set of very simple messages, the six levels. And this is what it looks like on the console. If you're a developer, this is probably what you'll see if you were to run that program. Couple things to note here, because some of these, you may notice, it may be obvious to you that a lot of these are related. Configuration is related to logging. And we're gonna talk in a minute about air handling. All very interrelated topics. So there's a separate presentation I think we could do in terms of proper air handling. The thing to be aware of is in the enterprise, it's very common to see a desire to just keep the process up no matter what. In Cloud Foundry, not so much. You're gonna let your processes die if they become corrupted or if a bad error occurs because the runtime is gonna start up a new one for you. It's much safer. You can't do that in place. This is again a case where you might wanna inject a switch via configuration that'll allow your Windows service, for example, to die gracefully if there is a big problem, which would be a critical event in the log, I would hope. Or a failure would be an exception. So we'll let it die in the cloud. We might try and keep it up again as long as it's on premise. Because our goal is to do all these things in place. So nothing changes today, you're just getting ready and making progress. When it comes to configuring the logging, this can get a little tricky. So there's a couple of links here in this presentation, especially if you're using one of the older versions of the CLR.NET framework, these are worth a read. Given 25 minutes, I don't have time to go into all the details, but these are good resources for that. I can assure you, if you're having problems and you wanna throw up your hands and say it doesn't work, I've done it. I've done it on all these versions. It absolutely does work. And if anyone has a problem, you can feel free to send me an email. Try to point you in the right direction. Which brings us to number five, air handling. So we just talked about how in the cloud, we're gonna let processes die that become corrupt because they'll get restarted for us. Cloud Foundry is gonna help us out that way. This is a great opportunity to go through that legacy application, the bigger the better, I guess, and take a look at how air handling is being implemented. There are well-defined best practices and worst practices by Microsoft for what to do around air handling. The one I'm showing here in the top left, as a consultant, I don't often get to say never do this. The answer is usually it depends to most complex questions, but this is almost always wrong. More than 99% of the time, you don't wanna do this. So in addition to the Microsoft best practices guidelines, my personal favorite reference is a CLR via C-Sharp. It's a pretty thick book by Jeff Richter. There's a couple chapters that relate to this. Absolutely fantastic, highly recommend. And each of these things, if done properly, I think it is fair to say, will improve the quality of that application, the quality of the code base. All right, if you felt cheated by number one, the dependencies, you said it wasn't really a way to improve things. That's planning, it's management, I don't like that. Well, here's number six. This is for the overachievers. You've already got that list of dependencies. So if you really wanna get ahead, pick a few of those and try and decouple from them, try and get some of those assets into NuGet, for example, whether you host it publicly or privately, try and work on that build pipeline. Having put that dependency list together, you're halfway here. Now it's just a question of implementation. So if you can do this, you pick something easy, something simple, migrated dependency, could be a gacked library, preferably something you control, typically the easiest ones. Get it into NuGet, update the references, test it out. Can you build it, does it pull? You're one step closer. I think we're at time. And I think we've hit all of our marks. We covered a framework. We talked about the pre-flight checklist. We went over the five ways, you can improve the application in place today. And those five to recap again are addressing dependencies, putting those lists together, updating to a reasonable and responsible version of the .NET framework, if you're not already there. Add and configure ability, typically via environment variables to begin with. Addressing logging, improving your logging and getting it to standard out as a first step. And addressing air handling. Those are our five. So thank you very much for your time today. I appreciate it. If you have any questions, I'll be at the Magenic booth, I think that's 128. And I believe we're trying really hard to give someone in the audience a Raspberry Pi on the way out. So, thank you.