Update 2021/2/10: Microsoft stopped creating images for Docker in the summer of 2020. We now publish artifacts, which can be used to spin up containers and BcContainerHelper has replaced NavContainerHelper. This blog post reflects the old way of using NAV/BC on Docker and references NavContainerHelper, which is outdated.
I don’t write blog posts about every single version of the NavContainerHelper, but I wanted to call out a few things in this version.
.app instead of .fob
Both NavContainerHelper and the ARM templates (http://aka.ms/getbc) has been using .fob files for setting up Business Central. Importing a .fob file, which contains code to setup test users and invoking this through Invoke-NavContainerCodeunit.
As many of you have discovered, Business Central CU4 and CU5 (and spring release) all have a bug, where Invoke-NavCodeunit does not work if you are using NavUserPassword authentication. This basically had the effect that all partners setting up test or demo environments through the ARM templates, started to run into problems.
This basically left me with 3 options:
- Stating that these versions could not be used with Docker and/or ARM templates in conjunction with AAD authentication or Test Users.
- Creating an ugly workaround for these versions.
- Accelerate the transformation of these .fob files to extensions and create a solution which will work in the future when we go to AL only.
I decided to go for #3 – and that is included in this version of NavContainerHelper.
The function Setup-NavContainerTestUsers is now downloading, publishing and installing an app. This app will expose an Api, which is invoked from PowerShell and after doings its job, the app is uninstalled and unpublished, leaving no trace.
The source for this app is here: https://dev.azure.com/businesscentralapps/CreateTestUsers
The other app is used for setting up the Azure Ad. This app is used in the ARM templates and the source can be found here: https://dev.azure.com/businesscentralapps/AzureAdAppSetup
Functions for invoking APIs
The new version has two new functions for communicating with APIs:
- Get-NavContainerApiCompanyId will return the Company Id to be used when invoking APIs
- Invoke-NavContainerApi will invoke an API.
The best way to describe the functions is to display how Setup-NavContainerTestUsers works in the new version:
$appfile = Join-Path $env:TEMP "CreateTestUsers.app" Download-File -sourceUrl "http://aka.ms/Microsoft_createtestusers_13.0.0.0.app" -destinationFile $appfile Publish-NavContainerApp -containerName $containerName -appFile $appFile -skipVerification -install -sync $companyId = Get-NavContainerApiCompanyId -containerName $containerName -tenant $tenant -credential $credential $parameters = @{ "name" = "CreateTestUsers" "value" = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))) } Invoke-NavContainerApi -containerName $containerName -tenant $tenant -credential $credential -APIPublisher "Microsoft" -APIGroup "Setup" -APIVersion "beta" -CompanyId $companyId -Method "POST" -Query "testUsers" -body $parameters | Out-Null UnPublish-NavContainerApp -containerName $containerName -appName CreateTestUsers -unInstall
Yes, I could have used Invoke-RestMethod directly, but then I would have to handle authentication and other things in all locations.
Invoke-NavContainerApi actually uses the IP address of the container to do the communication, and thus does not rely on updatehosts or other things being in place. It also disables SSL verification temporarily if the container uses SSL.
AL files
Another “new” feature is, that if you spin up your Business Central spring release container using -includeCSide and do not use -doNotExportObjectsToText, then you will now get three folders with source code:
- Original-<version>-<country> which is the classic CSide representation of objects
- Original-<version>-<country>-newsyntax which is the CSide representation of objects in new format.
- Original-<version>-<country>-al which is all objects in the newsyntax folder converted to AL and basically your baseline for AL objects.
If you copy the content of the AL folder to another folder and initialize a git repository on those files, then you can import your objects, reexport everything as AL and copy them on top of the baseline.
This will make git disover the changes in your objects.
Try to run this:
$imageName = "bcinsider.azurecr.io/bconprem:w1-ltsc2019" $myalfolder = "c:\temp\myal" $containerName = "temp" $credential = New-Object pscredential 'admin', (ConvertTo-SecureString -String 'P@ssword1' -AsPlainText -Force) New-NavContainer -accept_eula -containerName $containerName -imageName $imageName -includeCSide -licensefile C:\temp\license.flf -auth NavUserPassword -Credential $credential -updateHosts $version = Get-NavContainerNavVersion -containerOrImageName $containerName $baseAppFolder = "C:\ProgramData\NavContainerHelper\Extensions\Original-$version-al" if (Test-Path $myalfolder) { remove-item -Path (Join-Path $myalfolder "*") -Recurse -Force } else { New-Item $myalfolder -ItemType Directory } Set-Location $myalfolder & git init Copy-Item -Path (Join-Path $baseAppFolder "*") -Destination $myalfolder & git add . & git commit --message "baseapp"
Now open $myalfolder in VSCode and you will see all your files:
You should also see that the source control branch indicates no changes.
Now you import your C/AL object modifications (or just modify a few objects) and then run this script (Remember that everything must be compiled in C/Side):
$ModifiedFolder = Convert-ModifiedObjectsToAl -containerName $containerName -sqlCredential $credential -doNotUseDeltas Copy-Item -Path (Join-Path $ModifiedFolder "*") -Destination $myalfolder
As you can see this script just re-exports everything as AL and copies it on top of your baseline. Git will compare and discover what you actually changed:
This is by all means not perfect, but it is a starting point in finding your AL changes and starting to look at them and seeing whether it is right…
Enjoy
Freddy Kristiansen
Technical Evangelist
Pingback: Calling SOAP Services from PowerShell – James Pearson
I’am using your scripts for automated build on tfs/azure devops which uses invoke-navcontainercodeunit for test result export. Do you have an updated version of that using invoke-navcontainerapi?
LikeLike
I made an api page myself that invokes the test runner, but now i seems like UI Tests are not working. I guess UI gests do not work with api calls? Any suggestions? Can i use an install codeunit on the test extension that executes the tests? Do UI Tests work here?
I don’t like to rant about such issues but that is a very bad situation, we have branching policy in place and are not unable to merge to master because of failing tests and i don’t like to disable the tests i want to fix this asap.
LikeLike
In NavContainerHelper there is a function called Run-TestsInNavContainer. That is the one you should use. I did update my blogpost on CI (https://freddysblog.com/2018/11/12/developing-business-central-extensions-part-3/) on february 27th about this, but didn’t find the time to write a blog post yet.
LikeLike
and btw – the template repository has been updated as well. https://dev.azure.com/businesscentralapps/helloworld/_git/HelloWorld?path=%2Fscripts%2FRun-Tests.ps1&version=GBmaster – I will make some more updates to this going forward based on learnings – and blog posts will be updated.
LikeLike
Thank you that looks promising. I will update our own template then.
Is this method of test execution supported on older bc versions aswell? Fall 2018?
And where does the PsTestTool come from? currently i cannot find it in the container extension folder.
LikeLike
The pstesttool.ps1 is in NavContainerHelper, also the clientcontext.ps1 and the testpage.fob file and yes, that should work with NAV 2018 and up.
LikeLike
I implemented the changes in from the HelloWorld Repo and now i get this error:
CommunicationError : Error while copying content to a stream.
ClientSession in Error
at AwaitState, C:\ProgramData\NavContainerHelper\Extensions\lep-ext-dev\PsTestTool\ClientContext.ps1: line 98
at InvokeInteraction, C:\ProgramData\NavContainerHelper\Extensions\lep-ext-dev\PsTestTool\ClientContext.ps1: line 111
at InvokeInteractionAndCatchForm, C:\ProgramData\NavContainerHelper\Extensions\lep-ext-dev\PsTestTool\ClientContext.ps1: line 120
at OpenForm, C:\ProgramData\NavContainerHelper\Extensions\lep-ext-dev\PsTestTool\ClientContext.ps1: line 135
at Run-Tests, C:\ProgramData\NavContainerHelper\Extensions\lep-ext-dev\PsTestTool\PsTestFunctions.ps1: line 132
at , : line 33
ClientSession in Error
In C:\Program Files\WindowsPowerShell\Modules\navcontainerhelper\0.5.0.11\ContainerHandling\Invoke-ScriptInNavContainer.ps1:31 Zeichen:13
+ Invoke-Command -Session $session -ScriptBlock $scriptbloc …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (ClientSession in Error:String) [], RuntimeException
+ FullyQualifiedErrorId : ClientSession in Error
Should we continue the discussion on github navcontainerhelper?
LikeLike
You probably need to remove the -e webclient=N (which causes the container to be created without a webclient). Run-Tests uses the Client Services endpoint to connect and emulate a client for running the tests
LikeLike
Well i did that. In Fact i’am currently testing this locally not in the build agent.
For this i’am using a script “Local-Test” which basicly runs:
– compile-app.ps1
– publish-app.ps1
– run-tests.ps1
there i’am using the sandbox container for execution which obviously has the webclient enabled.
LikeLike
Ok, not sure what the reason is, use this all the time myself. Basically run-tests uses the client endpoint hosted with the web client to run.
Please open an issue here: https://github.com/Microsoft/navcontainerhelper/issues and include the output from the container generation and the error from run-tests as well. I do not monitor blog post comments as closely as github issues – and on blog post comments, I am the only person who can answer. On github issues – other people can chime in as well (and it is easier to get an overview)
LikeLike
Hi Freddyn
Do you know if the NavUserPassword authentication problem will be fixed in April CU1 …?
LikeLike
Are you referring to the Invoke-NavCodeUnit bug?
LikeLike
No I’m refering to UserName Auth. issue found in Fall CU4 and CU5 and April RTM (issue that is currently blocking new deployment on April Release at 1ClickFactory) .
LikeLike
Sorry, I am not aware of any issue on UserName auth – I use it all the time (also with April RTM)
LikeLike
Pingback: Calling SOAP Services from PowerShell - Dynamics 365 Business Central Community
Hello Freddy
I have a problem with the dockers. It’s a Web Services problem. When the Web Service is to read data, there is no problem, but when it is in the other sense tells me not found.
I understand that it is something of ports. The docker is mounted on an Azure virtual machine and I don’t know if it is necessary to redirect the ports of the machine itself to the docker.
Thank you
LikeLike
Didn’t see this until now – please use https://github.com/microsoft/navcontainerhelper/issues for issues, thanks
LikeLike
Oks thanks 😉
LikeLike
Pingback: Having Demo Data while developing Business Central Apps with Docker | Freddys blog
Pingback: Having Demo Data while developing Business Central Apps with Docker - Dynamics 365 Business Central Community
Pingback: Having Demo Data while developing Business Central Apps with Docker - Freddy's Blog - Dynamics 365 Business Central/NAV User Group - Dynamics User Group
Pingback: ArcherPoint Dynamics NAV Developer Digest - vol 237 - Microsoft Dynamics NAV Community