{"id":191,"date":"2025-09-16T06:19:45","date_gmt":"2025-09-16T06:19:45","guid":{"rendered":"https:\/\/blog.ivos.place\/?p=191"},"modified":"2025-09-16T06:19:48","modified_gmt":"2025-09-16T06:19:48","slug":"tmdl-demo-2-3-change-tracker","status":"publish","type":"post","link":"https:\/\/blog.ivos.place\/?p=191","title":{"rendered":"TMDL demo 2\/3: Change Tracker"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Disclaimer<\/h2>\n\n\n\n<p>This blog post is part of a three-part series based on my speaker session at the European Microsoft Fabric Community Conference 2025 (FabCon) in Vienna, which I had the pleasure of delivering together with my colleague <strong><a href=\"https:\/\/www.linkedin.com\/in\/rogerunholz\/\">Roger Unholz<\/a><\/strong>.<br><br>Our session, titled <em>\u201cTMDL Playoffs\u201d<\/em>, was a fast-paced showdown where we shared our favorite tips and tricks for working with TMDL. In this series, I\u2019ll take a deeper dive into each topic we presented on stage and explore them in more detail.All source files referenced throughout the posts are available in my public GitHub repository. <br><br><strong>Please note, <\/strong>that the scripts are not tested for every scenario, that they are only created for demonstration purposes and should <strong>never be applied on any running, productive enviroments. <\/strong><br>Link: <a href=\"https:\/\/github.com\/ivsch\/TMDLPlayoffs?utm_source=chatgpt.com\">ivsch\/TMDLPlayoffs<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Situation<\/h2>\n\n\n\n<p>For semantic models in Power BI, it is always recommended to maintain a proper version history. Now, I am not talking about version control\u2014we can already achieve that using TMDL and a proper repository. This post is about tracking the semantic model from a business point of view. This means that customers can see the changes to a report in a manual table, which could also be displayed in a Power BI report using the semantic model.<\/p>\n\n\n\n<p>Since I don\u2019t want to spend time re-investigating every small change I\u2019ve made to a semantic model, I still need a way for customers to clearly see what has changed in their semantic models.<\/p>\n\n\n\n<p>For this demo, I already have a Power BI semantic model saved in TMDL format with a manual table for the semantic model version (file is in GitHub folder &#8220;Ivo&#8221; &gt; &#8220;SM Change Tracker&#8221;). In the report view of the Power BI file, there is also a page showing the visual representation of the table.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"791\" height=\"420\" src=\"https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-2.png\" alt=\"\" class=\"wp-image-192\" srcset=\"https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-2.png 791w, https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-2-300x159.png 300w, https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-2-768x408.png 768w\" sizes=\"auto, (max-width: 791px) 100vw, 791px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Idea<\/h2>\n\n\n\n<p>As TMDL provides a proper definition of all objects within a semantic model, it is a solid basis for tracking changes from one version to another. What I\u2019d like to do is compare all the changes I have made in my local model with the one published in a Power BI workspace.<\/p>\n\n\n\n<p>For this comparison, I\u2019d like to follow this process:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Download the published semantic model via the Fabric REST API and store it locally.<\/li>\n\n\n\n<li>Compare the TMDL definitions and generate a difference report.<\/li>\n\n\n\n<li>Send the diff report to an LLM, which will summarize the differences in a clear, understandable way. The LLM should also classify each change as minor or major in order to properly increase the version number.<\/li>\n\n\n\n<li>Write the new version number and description back to the local semantic model. As the target table is a manual table, the data is stored within the TMDL definition and can easily be updated.<\/li>\n\n\n\n<li>Publish the updated report again to the destination (this step remains manual for now).<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"548\" src=\"https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-3-1024x548.png\" alt=\"\" class=\"wp-image-193\" srcset=\"https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-3-1024x548.png 1024w, https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-3-300x161.png 300w, https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-3-768x411.png 768w, https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-3-1536x822.png 1536w, https:\/\/blog.ivos.place\/wp-content\/uploads\/2025\/09\/image-3-2048x1096.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites for using the REST API<\/h2>\n\n\n\n<p>Before you can call the Microsoft Fabric API with a service principal (like in this demo), a few prerequisites need to be set up. First, create an app registration in Entra ID (this is your service principal). Then, generate a client secret and keep both the ID and secret safe \u2014 these will be used for authentication.<\/p>\n\n\n\n<p>Next, place the app registration into a security group that has access to Fabric\/Power BI. In the Azure Portal, link this security group to the app registration (important: this step is done in the Azure Portal, not just inside Entra ID). You\u2019ll also need the Tenant ID (and the <code>ctid<\/code> value mentioned in the setup) to include in your API calls.<\/p>\n\n\n\n<p>Finally, check the Power BI Admin settings to ensure service principals are allowed to use APIs. Without this toggle enabled, the service principal won\u2019t be able to access Fabric resources programmatically.<\/p>\n\n\n\n<p>In short: register the app, generate a secret, put it into the right security group, grab the tenant details, and confirm admin permissions. Once those are in place, the service principal can authenticate directly to the Fabric API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Solution<\/h2>\n\n\n\n<p>Because the process involves authentication, downloading model definitions, and file handling, a Jupyter notebook is used to orchestrate everything in one place. This could also run in any Jupyter environment, also directly inside Fabric to keep the workflow closer to the data and services.<\/p>\n\n\n\n<p>The following text represantion just descibes some part of the notebook code. The notebook is called compare-sm.ipynb and also available in the GitHub repository.<\/p>\n\n\n\n<p><strong>Download the published semantic model<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Authenticates against Fabric with the service principal (client ID, secret, tenant ID).<\/li>\n\n\n\n<li>Calls the Fabric API <code>getDefinition<\/code> to export the current published semantic model in TMDL format.<\/li>\n\n\n\n<li>Saves all model definition files into a <code>definition_published\/<\/code> folder locally.<\/li>\n<\/ul>\n\n\n\n<p><strong>Compare local vs. published versions<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Loads your local semantic model (from the <code>.pbip<\/code> project).<\/li>\n\n\n\n<li>Compares it line by line with the published TMDL version.<\/li>\n\n\n\n<li>Produces a structured diff report (<code>index.md<\/code>) showing what was added, removed, or changed.<\/li>\n<\/ul>\n\n\n\n<p><strong>Summarize changes with an LLM<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The diff report is sent to an Awan LLM (Meta-Llama-3.1).<\/li>\n\n\n\n<li>The LLM generates a short, business-friendly summary of the changes (max 50 words).<\/li>\n\n\n\n<li>It also labels the change as [MAJOR] (structural, new objects) or [MINOR] (renames, small tweaks).<\/li>\n<\/ul>\n\n\n\n<p><strong>Parse the LLM output<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Extracts the summary text and the change tag (MAJOR\/MINOR).<\/li>\n<\/ul>\n\n\n\n<p><strong>Update the version log inside the semantic model<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Re-encodes and writes the updated version history back into the TMDL file (with a backup saved).<\/li>\n\n\n\n<li>Opens the <code>Semantic Model Version.tmdl<\/code> file, which stores version history in a compressed payload.<\/li>\n\n\n\n<li>Reads existing version numbers and finds the last version.<\/li>\n\n\n\n<li>Bumps the version automatically:\n<ul class=\"wp-block-list\">\n<li>MAJOR \u2192 increments the major version (<code>1.3 \u2192 2.0<\/code>).<\/li>\n\n\n\n<li>MINOR \u2192 increments the minor version (<code>1.3 \u2192 1.4<\/code>).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Appends a new row with the new version number and the LLM-generated description.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer This blog post is part of a three-part series based on my speaker session at the European Microsoft Fabric Community Conference 2025 (FabCon) in Vienna, which I had the pleasure of delivering together with my colleague Roger Unholz. Our session, titled \u201cTMDL Playoffs\u201d, was a fast-paced showdown where we shared our favorite tips and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-191","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.ivos.place\/index.php?rest_route=\/wp\/v2\/posts\/191","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ivos.place\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ivos.place\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ivos.place\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ivos.place\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=191"}],"version-history":[{"count":5,"href":"https:\/\/blog.ivos.place\/index.php?rest_route=\/wp\/v2\/posts\/191\/revisions"}],"predecessor-version":[{"id":207,"href":"https:\/\/blog.ivos.place\/index.php?rest_route=\/wp\/v2\/posts\/191\/revisions\/207"}],"wp:attachment":[{"href":"https:\/\/blog.ivos.place\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=191"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ivos.place\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=191"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ivos.place\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}