Converting an ISV solution to an extension: Update 1

Well, what can I say……….this is going to take a while. 🙂

If you have no idea what I am talking about, start here.

After getting the objects converted to AL (using the standard process documented here), I was immediately greeted in VS Code by just over 8000 errors/warnings. Great start.

Luckily half of those were generated by the code analyzers. If you don’t know what they are, see here. Yes, at this point in the project I am considering that half of my issues are “only non-standard and poorly formatted code”. Gotta try and stay positive!

This brings me to my first “recommendation” in this process:

Turn off ALL the code analyzers.

The code analyzers are enabled by default, but can be turned off in your VS Code settings (see here). Once I get to a point where the “non-syntax” errors have been addressed, I will re-enable the analyzers and then deal with that barrel of fun. I’ve done this before in much smaller apps and very quickly got tired of typing ‘()’ 🙂

After disabling the analyzers my list of errors/warnings was brought “all the way down to” just over 4000. Oh wow I’m halfway done!! Ha ha.

The next thing I decided to tackle was another fairly easy one. Dealing with Product Group Code. If you’re not already aware, the Product Group Code feature was deprecated in favour of an improved parent/child Item Category structure. The Product Group Code fields however, were left in the product (not sure I like that, but we can deal with it) and marked for removal (see ObsoleteState property here). This causes any references to those fields to be flagged as warnings. Your code will still build, but this is a heads up that at some point in the future the fields will (probably!?) be removed. As I’m already head deep in things to fix and rework, I decided I’d rather not wait and I will remove references to those fields now.

For me, removing the references to the Product Group Code was pretty easy. We didn’t have major functionality tied to that field, but other ISV solutions may require more effort to remove those references, or, you may just choose to not deal with that now. Up to you.

That’s my update for now, back to fixing more issues. More updates to come!

Happy coding!

Advertisements

Converting an ISV solution to an extension: The Beginning

I’ve been a bit neglectful of this blog for the past couple of months, but with good reason. I’ve been jumping around multiple projects, and have now landed on a project that will involve taking a roughly 3000 object ISV solution and converting it to an extension.

Easy task? Far from it.
Challenging? Most definitely……but that’s why we do what we do right!?
Will I be able to convert it all to an extension? Not likely. (Yes, extensions still have limitations)

What I want to do is post my journey through this project. It’s not going to be a step by step of what to do as this is the first time I’m doing this. Up to this point, my extension experience was building them from scratch, or taking small pieces of existing features and recreating them as extensions. Nothing on this level yet. In all likelihood, I will make some mistakes, or perhaps do things in a less than ideal way, but that’s what I hope to capture in these posts, so that hopefully others can learn form them……and so I don’t make the same mistakes the next time I might have to do this. 🙂

Now first off, I want to be clear that having a 3000+ object extension is not ideal. It’s not the actual end-goal of this project. The overall solution will eventually be broken down into a set of smaller extensions, but as the current design and code is so tightly integrated, we’re moving it all to AL now and will break it apart later.

My “plan” (I use that word loosely) for this project when I began was to do the following:

  1. Convert all net new ISV objects to AL.
  2. Convert modified base tables and pages to AL (e.g. table/page extension files).
  3. Rework existing C/Side objects to move to an AL-based solution.

The first 2 steps leave me with a large portion of the solution in AL, but also a portion back in C/Side. The objects in C/Side would be the base codeunits, xmlports, reports, etc. that were modified by the ISV solution but can’t be directly converted to AL. That’s where step 3 comes into play. Reworking the objects to use events and/or redesigning the ISV solution is going to be required to clean up those objects. All in all, a lot of work, and I know for a fact that I will come across some design that cannot be replicated in an extension…..so stay tuned for however I’m going to deal with that!

Look for more posts on this as I move through this process. I hope to show both the good and the bad so that people can get a sense of what they’re in for if they come across a similar project.

Happy coding!

Dynamics 365 Business Central in an Azure Container Instance

I recently came across this article, which talks about using the Azure Cloud Shell to create an Azure Container Instance that runs Dynamics 365 Business Central. I was intrigued because Azure Container Instances has just recently been released to the public and I just gotta try the new stuff! Thanks Andrey for that article!

What is an Azure Container Instance you might be asking? If you’ve been keeping up with Dynamics 365 Business Central development, you have been using containers to create your environments. This requires Docker to run either on a Windows 10 or Windows 2016 Server machine that’s either hosted or on-prem. Either way, you’re carrying the overhead of the machine, physical or virtual. With Azure Container Instances, you can create the containers directly in Azure without that machine overhead. This ‘should’ translate to some sort of cost savings, but as my container has only been up for about 2 hours as of the time of this article, I don’t yet know if or how much savings there will be.

In Andrey’s post, he walks you through using the Azure Portal and Azure Cloud Shell to create the container. Being the ‘lazy developer’ that I am though, I prefer to do as little manual work as possible so I thought I’d take a stab at building PowerShell script that I can run locally and potentially automate the entire process. Yup, even opening my browser to paste the code into Azure Cloud Shell is apparently too much work for me. 🙂

Turns, out this is pretty easy to do. Using the Azure Resource Manager PowerShell module, we can easily connect to our Azure account, and create the necessary container pieces.

Here’s how…

Connect-AzureRmAccount
The first thing we need to do is connect to our subscription and tenant. The user will be prompted for credentials when this command is executed. If you don’t know what your subscription and tenant IDs are, you can find instructions here for the subscription ID, and here for the tenant ID.

New-AzureRmResourceGroup
Once we’re connected we need to create the Azure Resource Group that will be used for our container instance.

New-AzureRmContainerGroup
Once the resource group is created now we can create the container. This is where we get to set the parameters for the container. One change I made from Andrey’s initial post is that I assigned the container the DnsNameLabel, which will mean we can use the Fqdn to access the container instead of the IP address. If you’ve used FreddyK‘s NavContainerHelper module, you’ll also notice that the parameters here are similar to some of the ones used by the New-NavContainer commandlet. Hey maybe we can get some new additions to the module for this stuff!

Ok…..here’s the actual code. It’s pretty basic at this point in time. Just getting my feet wet to see how it goes.

Install-Module AzureRM

### SET VARIABLES
$azureSubID = ''
$azureTenantID = ''
$azureResourceGroupName = 'myResourceGroup'
$azureLocation = 'EastUS'
$containerEnvVariables = @{ACCEPT_EULA='Y';USESSL='N'}
$containerImage = 'microsoft/bcsandbox:us'
$containerName = 'myContainer'

### CONNECT TO AZURE ACCOUNT
Connect-AzureRmAccount -Environment AzureCloud -Force -Subscription $azureSubID -TenantId $azureTenantID

### CREATE RESOURCE GROUP
New-AzureRmResourceGroup -Name $azureResourceGroupName -Location $azureLocation

### CREATE CONTAINER
New-AzureRmContainerGroup -Image $containerImage `
 -Name $containerName `
 -ResourceGroupName $azureResourceGroupName `
 -Cpu 2 `
 -EnvironmentVariable $containerEnvVariables `
 -IpAddressType Public `
 -MemoryInGB 4 `
 -OsType Windows `
 -DnsNameLabel $containerName ​
 -Port 80,443,7048,7049,8080 `
 -Location $azureLocation

Once you execute the above script, go grab a coffee. After about 15-20 minutes your container should be up and running. You can check on the state of your container using the following code:

Get-AzureRmContainerGroup -ResourceGroupName $azureResourceGroupName -Name $containerName

When you run the above code you’ll see various properties of the container. What you want to pay attention to are ProvisioningState and State, which will appear as ‘Creating‘ and ‘Pending‘ as shown below.

InkedAzContainerDeploy1_LI

 

Once the container has been created, you should see the following statuses:

InkedAzContainerDeploy2_LI.jpg

 

Take note of the Fqdn property and save the address value. This is the address that you will need to use to connect to your Business Central environment later on.

Once your container has a State of ‘Running‘, you can check the container logs by using the following code:

Get-AzureRmContainerInstanceLog -ContainerGroupName $containerName -ResourceGroupName $azureResourceGroupName

Running the above code will show you the container logs, and again, if you’ve been using the NavContainerHelper, these logs will look very familiar to you:

InkedContainerLogs_LI

 

Remember!!!
When you connect to your container via Visual Studio Code or the Web Client, or to download the VSIX, you need to use the address from the FQDN property of the container instance, and not the address values that you see in the container logs. See some examples below:

Insiders
If you have access to the private insider builds for Business Central, you need to provide credentials in order to access the Docker image registry. You can do that by adding the ‘-RegistryCredential‘ parameter and supplying a PSCredential object to the New-AzureRmContainerGroup command.

Oh, if you’re into this kind of thing, you can check out the Azure Container Instance SLA here. It’s a super fun read! 🙂

Thanks again to Andrey Baludin for his original post on Azure Container Instances!

Happy coding!

AL Extensions: Translate Your Solution With the Multilingual App Toolkit Editor

In this post, I showed you how you can use Microsoft Dynamics Lifecycle Services to translate your AL project into a variety of languages.

As with most things, there are multiple ways to go about doing things. This post will look at the Microsoft Multilingual App Toolkit. This toolkit is integrated into Visual Studio but there is also a standalone version, called the Multilingual App Toolkit Editor.

With this tool you can manually do your translation and/or you can connect it to the Microsoft Translator service via Azure, which is what I will describe in this post.

Here’s how…

Download and install the Multilingual App Toolkit Editor.
https://developer.microsoft.com/en-us/windows/develop/multilingual-app-toolkit

If all you want to do is work offline and manually do your translations, you can stop here. Continue on if you want to connect to the translation service in Azure, but note that you do need an active Azure subscription for this.

Enable the Translator Text API in Azure.
Using the Azure portal, do the following to add the Translator Text API to your Azure subscription:

  1. Choose “Create a Resource“.
  2. Search for “Translator Text API” and select it.
  3. On the Translator Text API blade, press the Create button to begin configuring the subscription.
  4. Fill in the fields accordingly for the API by giving it a name, pricing tier, etc. Note that there is a free tier option that lets you translate up to 2 million characters per month. Additional pricing info can be found here.
  5. Press the Create button to deploy the API to your Azure subscription. This might take a few minutes.

Get your Translator Text API authentication key.
Once the API has been deployed, you need to get your subscription key so that the Multilingual Tool can authenticate and connect to it.

  1. In the Azure portal, select “All Resources” and select the Azure subscription that you deployed the API to.
  2. In the list of resources, click on the Translator Text API service that you deployed.
  3. In the Translator Text API blade, select Keys.
  4. Copy one of the 2 keys that are associated with the service. You will need this key value in the next step.

Add Multilingual App Toolkit Editor credentials.
Now that we have the Translator Text API up and running, and the Multilingual App Toolkit Editor installed, we need to configure the authentication. We do that using the Windows Credential Manager.

  1. On your Windows machine, launch Credential Manager.
  2. Select Windows Credentials.
  3. Select Add a generic credential.
  4. Enter the following values:
    • Internet or network address: Multilingual/MicrosoftTranslator
    • User name: Multilingual App Toolkit
    • Password: <the Translator Text API key that you retrieved earlier>
  5. Click OK.
  6. Close Credential Manager.

Ok, now that we have everything installed, deployed, and configured, we can open up the Multilingual App Toolkit Editor (search Multilingual Editor in your Start menu) and translate the XLF file from our AL project. You can learn about generating this file here.

  1. Copy the auto-generated ‘.g.xlf‘ file to create a new file in the same folder. Rename the file based on the AL standards here.
  2. Edit the new file and update the ‘target-language‘ property to be the language that you are translating the file to (e.g. fr-CA).
  3. Close and save the file.
  4. Using the Multilingual App Toolkit Editor, select Open and select your new file.
  5. From the ribbon, select Translate > Translate All. The toolkit will now use the Translator Text API in Azure to translate the file based on the source and target languages. This might take a few minutes based on the numbers of items that need to be translated in your solution.
  6. Once the translation is done you can manually review and edit any if you wish.
  7. Close and save the file.

Now you have your new translation file. Simply repeat the steps to generate each translation that your AL solution requires!

Submitting to AppSource
If you are submitting your solution to AppSource, even if you do not need multi-language support in your solution, you still must provide (at a minimum) a translation file for the base language (e.g. en-US) in which your solution is published.

Note that the auto-generated ‘.g.xlf’ file is NOT a properly formatted translation file and your solution will not pass validation if you do not create at least the base language file.

In the pic below you have the raw ‘.g.xlf’ file as it gets created during the AL project build process. As you can see, there is only a ‘source‘ property for the message control even though the ‘target-language‘ of the file is set to ‘en-US’:

AutoGenTranslationFileFormat

In a properly formatted translation file, you will have both the ‘source‘ and the ‘target‘ properties:

ProperTranslationFileFormat

In addition to the formatting, you can’t rely on editing the ‘.g.xlf’ file because it gets overwritten each time you build your AL project.

In short, use the ‘.g.xlf’ file ONLY as the source for generating other translation files.

EDIT: I was just informed that fellow MVP Tobias Fenster‘s ALRunner VS Code extension can (among many things) convert the ‘.g.xlf’ translation file into a properly formatted file. Quick and easy! Check it out here!

Happy coding!

AL Extensions: Managing Version Numbering With VSTS Builds

I wanted to do this post to talk a bit more about something that Soren Klemmensen had in Part 2 of the blog series on automated builds with VSTS. We wanted to keep those original posts as simple as possible but there are a few more topics that we would like to expand upon.

In that particular post, he covered the PowerShell code needed to generate the AL extension using the ALC compiler tool, and in there is some interesting code around setting the version number of the extension package that gets created. That’s what I want to talk a bit more about here.

As we know, the version number that is assigned to the AL extension needs to be stored in the app.json that sits in the the AL project, and that value needs to be there before the compile process can be executed, like this:

ALExtensionVersion

Typically, this means that the developer is updating that value as needed before they compile and package the extension.

Wouldn’t it be nice to not have to have the developer do that every build? They’ve already got so much to worry about! 🙂

Well…it just so happens that one of the many great features in VSTS build definitions is the ability to automatically generate build numbers. Using a combination of static values and tokens, you’re able to generate any build number that you would like and in pretty much any format you would like.

You can read up on what tokens are available here, and how you can use them to build whatever format of build number you want.

In VSTS, these build numbers can be used to tag checkins so that you can track which checkin represents a build, but we’re going to leverage this and build ourselves a properly formatted version number that we will inject into the AL extension during the build process. This way, the developer can sit back and relax wait for the VSTS magic to happen!

Here’s how we’re going to do it…

1. Update build definition

We need to update our build definition to create our version number for us. We do that by populating the Build number format box on the Options tab of the build definition. Here’s where you can use the VSTS tokens and/or static text.

For this walkthrough, my version number needs to meet the following criteria:

  1. It will follow the standard #.#.#.# formatting that is required for an AL extension.
  2. It will be based on the date that the build is executed.
  3. I need to ensure I always get a unique version number even if I generate multiple builds in a day.
  4. This is the first version of my extension, so I want to make sure the version number begins with 1.0.

Given all of this criteria, I’ll populate the field like this:

BuildVersionFormat

As you can see, I am just hard-coding the “1.0.” so that every version I get begins the same. I’m using the date token (note the format can be changed if you like) so that the version contains the date on which the build is executed. Last but not least, I’m using the revision token to ensure that every time the build is executed I will get a unique version number. If you’re implementing Continuous Integration (and you really should!) you will certainly at some point have multiple builds done within the same day.

NOTE: The revision token is 1-based, so in my example the first build on a given day will end with .1 and not .0 (not what I would have preferred but what can you do).

2. Update build script

Remember I mentioned Soren’s post? In that post the following code is added to your build script. We only really need to update 1 line in this script, but I do want to talk about a few of the other lines so you know what’s going on here. The specific code we’re interested in is shown in red:

$ExtensionAppJsonFile = “.\app.json”
$ExtensionAppJsonObject = Get-Content -Raw -Path $ExtensionAppJsonFile | ConvertFrom-Json
$Publisher = $ExtensionAppJsonObject.Publisher
$Name = $ExtensionAppJsonObject.Name
$ExtensionAppJsonObject.Version = ‘1.0.’+$env:Build_BuildID + ‘.0’
$ExtensionName = $Publisher + ‘_’ + $Name + ‘_’ + $ExtensionAppJsonObject.Version + ‘.app’
$ExtensionAppJsonObject | ConvertTo-Json | set-content $ExtensionAppJsonFile
$ALProjectFolder = $env:System_DefaultWorkingDirectory
$ALPackageCachePath = ‘C:\ALBuild\Symbols’
Write-Host “Using Symbols Folder: ” $ALPackageCachePath
$ALCompilerPath = ‘C:\ALBuild\bin’
Write-Host “Using Compiler: ” $ALCompilerPath
$AlPackageOutPath = Join-Path -Path $env:Build_StagingDirectory -ChildPath $ExtensionName
Write-Host “Using Output Folder: ” $AlPackageOutPath
Set-Location -Path $ALCompilerPath
.\alc.exe /project:$ALProjectFolder /packagecachepath:$ALPackageCachePath /out:$AlPackageOutPath

 

The first 2 lines of highlighted code read the app.json file from the AL project into a PowerShell object using ConvertFrom-Json. This will allow us to read/write any of the properties within the json file:

$ExtensionAppJsonFile = ".\app.json"
$ExtensionAppJsonObject = Get-Content -Raw -Path $ExtensionAppJsonFile | ConvertFrom-Json

Now what we want to do is update the Version property with the build number that our VSTS build definition generated. We can get that value by using one of the environment variables that VSTS makes available while the build process is running. There are a bunch of environment variables available to us (see here for more info) but the one that we’re interested in is Build.BuildNumber. The value in this variable is the value that VSTS generated based on the Build number format that you populated in your build definition. If you leave that field blank, then this environment variable would be empty.

This is where we need to replace some of the code from Soren’s post. The original code (shown below) sets the version number to Build.BuildID, which is the identifier that VSTS assigns to each build:

$ExtensionAppJsonObject.Version = ‘1.0.’+$env:Build_BuildID + ‘.0’

Since we want to control the version number format using our build definition, this is where we need to make a small change. We’ll replace the above line of code with the following that will update the Version property to Build.BuildNumber:

$ExtensionAppJsonObject.Version = $env:Build_BuildNumber

Notes

  • To access VSTS environment variables in your PowerShell script, begin the variable name with ‘$env:’ and replace ‘.’ with ‘_’.
  • Because this environment variables are only generated during the build process you will not be able to manually run your PowerShell script to test the values of these variables.

Finally, we need to write our changes to the app.json file as so far we’ve only made the changes in the PowerShell object We use ConvertTo-Json for that:

$ExtensionAppJsonObject | ConvertTo-Json | set-content $ExtensionAppJsonFile

That’s It!

After you’ve done the above updates to your build definition and script, when you generate your build either manually or via trigger, your packaged AL extension will have version numbers similar to the following:

1.0.20180407.1   (first build of the day)
1.0.20180407.2   (second build of the day)
1.0.20180407.3   (third build of the day)
1.0.20180407.n   (nth build of the day)

Source code management

One of the obvious questions if you implement your build process like this is that if the version number is only updated at the time the build is done, what happens with source code management? Well to be honest, I do not track the version number of my extension in source code management at all.

Personally, I don’t see the value since when my build runs, it tags the checkin that represents that build, so at any point in time I can go back and find the source code for any build that I’ve done, and if I need to I can rebuild it.

So what do I keep in source code management? I keep all of my extension versions permanently set to ‘0.0.0.0’. This means that no developer ever needs to touch the version property during the development stage. It also helps identify if a database has a released version of my extension installed versus a pre-release development version, which does happen from time to time.

Hopefully you’ve found this useful and you’ve now updated your build definition to provide you the exact version number format that you want.

For a listing of the all the automated build articles, see my post here.

That’s it for now, happy coding!

AL Extensions: Automated Builds with VSTS

Hello there,

I recently spent a few hours with Soren Klemmensen, whom I have known for many years. He was actually the one who introduced me to source code management, at which I swore I’d never be happy using it because it was just “development overhead”. Story for another day, but rest assured, that’s not at all how I feel now. I cannot live without Visual Studio Team Services (VSTS)!

In any case, aside from catching up on things, we decided to put together a series of articles on automating builds using VSTS and AL Extensions.Having been the person who has spent countless hours manually doing “the build”, I cannot express how happy I was to push all of that off to automation so that “it just happens”.

Below is where you can find the articles. They’ll walk you through setting up a simple build to start, and from there we’ll build on it with some more advanced topics, such as having VSTS sign your app during the build!

Part 1: How to setup a build agent in VSTS

Part 2: Prepping your build server for creating Apps from source

Part 3: Creating a build definition

Part 4: Preparing your build server for code signing

Part 5: Updating your build server for code signing

There you have it, the first articles covering automating builds for AL extensions using VSTS.

Also, don’t forget that you can sign up for a VSTS account for free, and with that account you can have up to 5 developers use it, so for some, you just might be able to make use of the free account! There’s really no reason to not use it!

That’s all for now, this is not the end of the articles on this topic, so keep an eye out here and on Soren’s blog for more information in the future.

Happy coding….

Dynamics 365 Business Central

Hey everyone!

I’m off enjoying the March Break with my family this week but I wanted to get this out, since it’s (finally!) been announced by Microsoft that the product formerly known as Dynamics ‘Tenerife’ will be made available April 2, 2018 under the name Dynamics 365 Business Central.

As the announcement states, it’s going to be available April 2, 2018 to 14 countries: United States, Canada, United Kingdom, Denmark, Netherlands, Germany, Spain, Italy, France, Austria, Switzerland, Belgium, Sweden, and Finland. Australia and New Zealand will be available on July 1, 2018. Take note that you must purchase Dynamics 365 Business Central through a Cloud Solution Provider (CSP) partner.

I don’t have much else to say right now, other than I’ve been working with Dynamics 365 Business Central for a while now and I think everyone’s going to love it! This product is moving fast and the advancements being made are amazing! Definitely some very exciting times ahead!

If you want to read more, check out the Dynamics 365 Business Central website here.

Until next time….happy coding!