PowerShell

Interpreting compiler results in D365FO using PowerShell

When you build your code, the results are hard to interpret and are being capped at 1000 entries per category within the Visual Studio error pane. The compiler does generate output files with more valuable content within each package. We have written PowerShell code for analyzing and interpreting compiler results of Microsoft Dynamics 365 for Finance and Operations in a more meaningful way.

The BuildModelResult.LOG and XML files have the details of your errors, warnings and tasks. Running the following script parses these files and counts the warnings and errors, to get a better idea of the remaining work during your implementation and upgrade:

$displayErrorsOnly = $false # $true # $false
$rootDirectory = "C:\AOSService\PackagesLocalDirectory"

$results = Get-ChildItem -Path $rootDirectory -Filter BuildModelResult.log -Recurse -Depth 1 -ErrorAction SilentlyContinue -Force
$totalErrors = 0
$totalWarnings = 0

foreach ($result in $results)
{
    try
    {
        $errorText = Select-String -LiteralPath $result.FullName -Pattern ^Errors: | ForEach-Object {$_.Line}
        $errorCount = [int]$errorText.Split()[-1]
        $totalErrors += $errorCount

        $warningText = Select-String -LiteralPath $result.FullName -Pattern ^Warnings: | ForEach-Object {$_.Line}
        $warningCount = [int]$warningText.Split()[-1]
        $totalWarnings += $warningCount

        if ($displayErrorsOnly -eq $true -and $errorCount -eq 0)
        {
            continue
        }

        Write-Host "$($result.DirectoryName)\$($result.Name) " -NoNewline
        if ($errorCount -gt 0)
        {
            Write-Host " $errorText" -NoNewline -ForegroundColor Red
        }
        if ($warningCount -gt 0)
        {
            Write-Host " $warningText" -ForegroundColor Yellow
        }
        else
        {
            Write-Host
        }
    }
    catch
    {
    Write-Host
    Write-Host "Error during processing"
    }
}

Write-Host "Total Errors: $totalErrors" -ForegroundColor Red
Write-Host "Total Warnings: $totalWarnings" -ForegroundColor Yellow

The compiler results are displayed in the following format as an overview:

compiler results

If you want to do a detailed analysis, we also have PowerShell scripts prepared for extracting the errors and saving them in a CSV file for better processing. Opening it with Excel allows you to format them into a table.

We typically copy all error texts to a new sheet, run the duplicate entries removal, then do a count on what is the top ranking error to see if we have any low-hanging fruits to be fixed.

=SUMPRODUCT(('JADOperation-Warning'!$D$2:$D$1721=Sheet1!A2)+0)

You could be very efficient about deciding what things to fix first and next, and is easier to delegate tasks this way.

Source code for all four scripts are available on GitHub.

Archiving SQL database backups using Azure blob storage

It is a good practice to keep multiple copies of our most precious data. By using on-premises SQL Server databases for AX 2012 or Dynamics 365 Finance and Operations, archiving SQL database backups to offsite-locations are a must. I have built automation for archiving SQL database backups using Azure Blob Storage.

Overview of the processes

Maintenance regime

Our maintenance regime looks like the following:

  • 1x Weekly Full backup
  • 6x Daily Differential backup
  • 15 minute Transactional log backups

They are captured locally on the primary SQL instance, to keep the timestamps for last successful backups in our AlwaysOn cluster. Then we move the files to a shared network storage, which is visible to both High Availability sites, in case there is an outage and we need to a fail over and restore data.

In case of a natural disaster due to the close geographical proximity of the sites we needed an additional layer of safety.

Archiving offsite

Every night we are running a PowerShell script that uses the AzCopy utility. It is uploading our backup files on a Microsoft Azure cloud storage account.

You are paying for the network usage (IO) and the size occupied on the disks, so it is a good idea to have some sort of housekeeping. Our solution was to use an Azure RunBook to determine what to keep and what to delete. The current setup is to have 1 full backup file for a year available every quarter (4x 400 GB), and keep all full / differential / transactional files for the last month (4x 400 GB + 26x 10 GB).

This puts the total size occupied around 4 TB, and costs about 35 GBP a month using a cold storage. This price could go up if you also want to utilize a hot tier storage for the latest backup set. That is useful if you want to come back from the cloud on a short notice.

(more…)

Change AOT objects without AX client

During the #MSDyn365FO upgrade code cleanup exercise for Best Practice errors one of our forms got corrupted. Compilation has caused a crash, and when I tried to reopen it then it went in an endless compile loop. I had to find a way to change AOT objects without AX client.

This could be achieved by various different solutions. One option is using the SysStartupCmd framework to import a corrected XPO with the AOTimportFile startup command. Also you could try removing the objects from the ModelElement and ModelElementData tables within the AX2012_model ModelStore DB. Another solution is to go around using the client.

I went with the last option, and used a client-less approach via talking directly to the Application Object Server through the Business Connector interface. Here is a simple PowerShell script I have implemented that uses reflection for the AOT elements, where I could access a SysTreeNode object and then delete it:

# Instantiate Business Connector proxy object and sign on
Add-Type -Path "C:\Program Files\Microsoft Dynamics AX\60\BusinessConnector\Bin\Microsoft.Dynamics.BusinessConnectorNet.dll"
$ax = new-object Microsoft.Dynamics.BusinessConnectorNet.Axapta
$ax.logon('','','','','','')

# List commands
$ax | Get-Member

# Get the incorrect form object and delete the treenode
$node = $ax.CallStaticClassMethod('SysTreeNode', 'newTreeNodePath', '\Forms\CCMOrderPadActivityMK2')
$node.Call('name')
$node.Call('delete')
Change AOT objects without AX client using Business Connector in PowerShell

This is how you could change AOT objects without AX client in a fast, safe and easy way. BC is still a very powerful way of running code on-the-fly. A similar approach was applied when we wanted to validate if AIF ports were up and running on our AOS instances earlier.

By |2020-03-23T13:37:18+01:00September 11th, 2018|Categories: AX 2012|Tags: , , , , , |0 Comments

Validate AIF ports if they are running in AX

It may happen that AIF ports are failing due to an error, or they do not activate when the AX AOS instance gets started. There is a way to validate AIF ports if they are running correctly using a combination of X++ code and a PowerShell script that you may find below.

Add the following to the Global class collection and do a Full CIL:

public static client boolean WIK_notStartedAIFPortExists()
{
    #Aif
    AifInboundPort  port;
    Map             serviceStatusMap;
    str             serviceTypeName, status;

    boolean ret             = false;
    boolean isPortStarted   = false;

    serviceStatusMap = appl.getServiceHostStatus();


    if (serviceStatusMap)
    {
        while select Name from port
        {
            serviceTypeName = strFmt('%1.%2', #DotNetServiceNamespace, port.Name);

            if (serviceStatusMap.exists(serviceTypeName))
            {
                status            = serviceStatusMap.lookup(serviceTypeName);
                isPortStarted     = strStartsWith(status, #WcfCommunicationState_Opened);

                if (!isPortStarted)
                {
                    ret = true;
                    break;

                }
            }
        }
    }

    return ret;
}

And here is the PowerShell script to validate AIF ports. Please make sure you provide a comma-separated list for the AOS names, and a correct path where the Business Connector DLL could be found. Also you might want to provide credential details for the AX login if the user running the script does not have access.

# Set variables

$computerName = "TESTAOS01,TESTAOS02" -split ','
$AXBCpath = "C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Microsoft.Dynamics.BusinessConnectorNet.dll"

# Validate Administrative privileges and Elevated command prompt

Write-Host "Validating security privileges... " -NoNewline

if (-NOT [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544"))
{
    throw "You must be running the script in an Elevated command prompt using the Run as administrator option!"
}

if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
    throw "You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator!"    
}

Write-Host "Done" -ForegroundColor Yellow

# Script for checking AIF

[ScriptBlock] $global:CheckAIFPorts =
{
    param ([string] $AXBCpath)
    try
    {
        $bcassembly = [Reflection.Assembly]::Loadfile($AXBCpath)      
        $ax = new-object Microsoft.Dynamics.BusinessConnectorNet.Axapta
        $ax.logon("","","","")
        $xSession = $ax.CreateAxaptaObject("XSession")   
        $AOSName = $xSession.call("AOSName")
        
        Write-Host -NoNewline "$AOSName AIF ports are ... "
        if ($ax.CallStaticClassMethod("Global", "WIK_notStartedAIFPortExists") -eq $false)
        #if ($ax.CallStaticClassMethod("Global", "isaos") -eq $true) #"WIK_notStartedAIFPortExists")
        {
            Write-Host "OK" -ForegroundColor Yellow
        }
        else
        {
            Write-Host "NOT OK" -ForegroundColor Red
        }
        $logedOff = $ax.logoff()
    }
    catch [Exception]
    {
	    Write-Host "Failed" -ForegroundColor Red
	    Write-Host $_.exception.message
    }
}

$Session = New-PSSession -ComputerName $computerName
Invoke-Command -Session $Session -ScriptBlock $CheckAIFPorts -ArgumentList "$AXBCpath"
Remove-PSSession -Session $Session

validate AIF ports

 

By |2016-08-17T09:02:35+02:00August 17th, 2016|Categories: AX 2012|Tags: , , , , |2 Comments

PowerShell and AXModel files

In AX 2012 there is a very high chance that you have to work with the new AXModel files.

You may receive an ISV solution, or a Microsoft hotfix and you would like to know what is inside. If you have installed the management extensions, you can go to Start menu > Administrative tools > Microsoft Dynamics AX 2012 Management Shell to open PowerShell to access the AX-specific utilities.

We have a hotfix in our example, and would like to see what objects are inside, which may cause a conflict with out customizations. You can run the following command to get more details from our AXModel hotfix:

(more…)

By |2015-11-23T17:53:12+01:00October 9th, 2012|Categories: AX 2012|Tags: , , , , |0 Comments