Tip: Test for Tables Missing from Permission Sets

In PowerShell:

$tablesInPermissionSets = @()

$permissionSets = gci . -Recurse -Filter '*.al' | ? {(gc $_.FullName).Item(0).startsWith('permissionset')}
 $permissionSets | % {
    $content = gc $_.FullName -Raw
    [Regex]::Matches($content, '(?<=tabledata ).*(?= =)') | % {
        $tablesInPermissionSets += $_.Value

$tablesInTables = @()

$tables = gci . -Recurse -Filter '*.al' | Where-Object {(Get-Content $_.FullName).Item(0).StartsWith('table ')}
 $tables | % {
    $content = gc $_.FullName -Raw
    [Regex]::Matches($content, "(?<=table \d+ ).*(?=$([Environment]::NewLine))") | % {
        $tablesInTables += $_.Value

$missingTables = ""

Compare-Object $tablesInTables $tablesInPermissionSets | ? SideIndicator -eq '<=' | % {
    $missingTables += $_.InputObject + [Environment]::NewLine

if ('' -ne $missingTables) {
    throw "Missing table permissions: $missingTables"

In English:

  1. Find all the files in the current folder, and child folders, with a filename ending in .al and which have a first line starting with “permissionset”
  2. Build a collection of the tabledata objects that are referenced in those permission sets
  3. Find all the files in the current folder, and child folders, with a filename ending in .al and which have a first line starting with “table ” (with a space to avoid matching “tableextension”)
  4. Build a collection of the names of the tables
  5. Use Compare-Object to compare the collections and find names which appear in the list of tables but not in tabledata permissions
  6. Build an error message of missing table permissions
  7. Throw the error

PowerShell Profile:

Like most small PowerShell scripts that I write, I’ve just added it to my PowerShell profile. Run code $profile in a PowerShell prompt to open the profile file in VS Code.

function Test-Permissions() {
  #...all of the above code

Maybe there is already a VS Code extension that checks for this? It would make sense, but I’m pretty minimalist with the extensions that I have installed anyway. I run it from the terminal in VS Code.

Tip: Get Current Callstack with a Collectible Error

The Code

codeunit 50104 "Get Callstack"
    SingleInstance = true;

    procedure GetCallstack() Callstack: Text
        LF: Char;
        LF := 10;
        Error(ErrorInfo.Create('', true));
        Callstack := GetCollectedErrors(true).Get(1).Callstack;
        exit(Callstack.Substring(Callstack.IndexOf(Format(LF)) + 1));

Yea, but…why?

I dunno, I was just curious whether it was possible. And, it is 🧐 Any sensible applications are probably going to be do with error handling or reporting.

You may be tempted to have your code respond differently depending on the context in which it has been called and read the callstack for that purpose. That’s not a train you want to ride though. I’ve tried, it stops at some pretty weird stations.

One advantage of this approach over using a TryFunction (as below) is that the debugger doesn’t break on collectible errors. It can sometimes be frustrating stepping through errors that are always caught to get to the code that you actually want to debug.

procedure LessGoodGetCallstack(): Text

procedure ThrowError()

Tip: List-Commits

function List-Commits {
  cd 'C:\Git'
  $Commits = @()
  Get-ChildItem . -Directory | % {
    cd "$_"
    if (Test-Path (Join-Path (Get-Location) '.git')) {
      $Commits += git log --all --format="$($_)~%h~%ai~%s~%an" | ConvertFrom-Csv -Delimiter '~' -Header ('Project,Hash,Date,Message,Author'.Split(','))
    cd ..
  $Commits | ? Author -EQ "$(git config --get user.name)" | sort Date -Descending | Out-GridView -Title 'Commits'

This function iterates through Git repositories under the same parent folder (C:\Git in my case), builds a list of all the commits that you’ve authored (i.e. that match your user.name in Git config) and displays them in descending date order in a grid view.

Change the path in the second line to suit, or just remove it to have it search for repositories under the current directory.

Sample output in a PowerShell grid view

I use it to remind myself what I’ve been working on over the last few days. Mostly for fun and only occasionally because I’m late filling in my timesheets… 🙄

Tip: Remove-BranchesWithUpstreamGone

Wait, I Thought I Deleted That Branch?

One of the things that I found counter-intuitive when I was getting started with Git is that when branches are deleted on the server they are still present in your local repository, even after you have fetched from the server.

We typically delete the source branch when completing a pull request, so this happens a lot. Usually, once the PR has been completed I want to:

  1. remove the reference to the deleted remote branch
  2. remove the corresponding local branch

Removing Remote Reference

The reference to the remote branch is removed when you run git fetch with the prune switch.

git fetch --prune
Fetching origin
From <remote url>
- [deleted] (none) -> origin/test

Removing Local Branches

Local branches can be removed with the git branch command. Adding -d first checks for unmerged commits and will not delete the branch if there are any commits which are only in the branch that is being deleted. Adding -D overrides the check and deletes the branch anyway.

git branch -d test
Deleted branch test (was <commit hash>)


I’ve added a couple of PowerShell functions to my profile file – which means they are always available in my terminal. If I’m working on an app and I know that some PR’s have been merged I can clean up my workspace running Remove-BranchesWithUpstreamGone in VS Code’s terminal.

As a rule, I don’t need to keep any branches which used to have a copy of the server, but don’t any more (indicated by [gone] in the list of branches). Obviously, local branches which have never been pushed to the server won’t be deleted.

function Remove-BranchesWithUpstreamGone {
  (Get-BranchesWithUpstreamGone) | ForEach-Object {
    Write-Host "Removing branch $_"
    git branch $_ -D

function Get-BranchesWithUpstreamGone {
  git fetch --all --prune | Out-Null
  $Branches = git branch -v | Where-Object { $_.Contains('[gone]') }
  $BranchNames += $Branches | ForEach-Object {
    if ($_.Split(' ').Item(1) -ne '') {
      $_.Split(' ').Item(1)
    else {
      $_.Split(' ').Item(2)


Tip: Case Insensitive Glob Patterns

I must admit I hadn’t heard of glob patterns until seeing some settings in VS Code that use them. They are a way of defining a pattern that matches one or more files and folders. For example, the Text Editor section of the settings (UI) in VS Code allows you to exclude files and folders from the file explorer with glob patterns and gives you this useful link for more information.

My AL Test Runner extension has a glob pattern setting to identify your test codeunits (used when identifying the file path and line number of a failing test to give you a clickable link in the output window).

Turns out that glob patterns are case-sensitive. You can, however, match from a group of characters (like regular expressions) with square brackets. Useful if you’ve got inconsistently named files. My Test Codeunit Glob Pattern setting (in AL Test Runner) is set to **Tests/*.[cC]odeunit.al i.e. match files in a Tests folder which end with either codeunit.al or Codeunit.al