{"id":16906,"date":"2023-12-28T08:32:58","date_gmt":"2023-12-28T08:32:58","guid":{"rendered":"https:\/\/dynamics.folio3.com\/blog\/?p=16906"},"modified":"2024-01-01T08:09:13","modified_gmt":"2024-01-01T08:09:13","slug":"microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd","status":"publish","type":"post","link":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/","title":{"rendered":"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD"},"content":{"rendered":"<h1><strong>Business Central Manual Package Deployment <\/strong><\/h1>\n<p>Microsoft has developed Business Central as an Enterprise Resource Planning (ERP) system. When we mention a Software as a Service (SaaS) deployment of Business Central, we are referring to a cloud-based deployment model. In this model, Microsoft hosts and manages the software on their servers, and users access it over the internet through a web browser. After completing the development and testing phases of your tenant customization, the crucial next step is deploying the extension, encapsulated in the <strong>.app file<\/strong>, to your customer&#8217;s tenant. To execute this successfully, sign-in as a user with the necessary permissions to access the <strong>Extension Management<\/strong> page on environment. The deployment process involves going to navigation, clicking <strong>Manage<\/strong> and selecting the &#8220;<strong>Upload Extension<\/strong>&#8221; action, allowing you to upload the extension for either the current or the subsequent version of the service.<\/p>\n<p><strong>Permission Required to Deploy Extension on Business Central Packages:<\/strong><\/p>\n<p>Microsoft does not categorize <strong>EXTEN. MGT. \u2013 ADMIN<\/strong> under specific permission sets. However, to install or uninstall extensions, you need to be part of the D365 Extension Mgt. user group or explicitly possess the EXTEN. MGT. \u2013 ADMIN permission set. (For earlier versions, having the D365 EXTENSION MGT permission set is necessary).<img fetchpriority=\"high\" decoding=\"async\" class=\"aligncenter size-full wp-image-16958\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_001.jpg\" alt=\"\" width=\"1845\" height=\"488\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_001.jpg 1845w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_001-300x79.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_001-1024x271.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_001-768x203.jpg 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_001-1536x406.jpg 1536w\" sizes=\"(max-width: 1845px) 100vw, 1845px\" \/><img decoding=\"async\" class=\"aligncenter size-full wp-image-16907\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_002.jpg\" alt=\"\" width=\"1296\" height=\"622\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_002.jpg 1296w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_002-300x144.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_002-1024x491.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_002-768x369.jpg 768w\" sizes=\"(max-width: 1296px) 100vw, 1296px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-01<\/p>\n<h1><strong>Business Central Package Deployment using Azure DevOps CI\/CD<\/strong><\/h1>\n<p>Creating a modern way to develop Microsoft Dynamics 365 Business Central with Azure DevOps is a smart choice. It helps to stay up-to-date, especially with the monthly changes in the SaaS world. However, it&#8217;s important to note that there isn&#8217;t a standard way to set up Azure DevOps, and there&#8217;s no built-in process for continuous integration and deployment. We can make it easy to deploy the <strong>Business Central package using CI\/CD<\/strong>. We will start with <strong>Microsoft Business Central Automation APIs<\/strong> that allow us to automate the setup of companies through APIs. Once the tenants are created, we can use the automation APIs to set up the tenant as needed. This typically involves creating a new company, installing extensions, adding users to user groups, and assigning permission sets to users. These APIs can be accessed by delegated admin credentials and Business Central users who have the necessary permissions. Automation APIs are placed in the <strong>microsoft\/automation<\/strong> API namespace.<\/p>\n<p>To initiate the use of Automation APIs for Package deployment through CI\/CD, certain initial configurations are required:<\/p>\n<ol>\n<li>Activate OAuth 2.0 for Microsoft Dynamics 365 Business Central.\n<ul>\n<li>Create New App E.g. &#8220;Folio3BusinessCentralApp&#8221;.<\/li>\n<li>Grant API Permissions.<\/li>\n<li>Create Client Secret keys.<\/li>\n<li>Getting Keys for Token API calls.<\/li>\n<\/ul>\n<\/li>\n<li>Register the Azure Active Directory in Dynamics 365 Business Central.<\/li>\n<li>Call OAuth 2.0 Token API from Postman.<\/li>\n<li>Employ Automation APIs.<\/li>\n<li>Set up the Azure DevOps Project for CI\/CD.<\/li>\n<\/ol>\n<p>PowerShell Scripts for Release Pipeline Tasks.<\/p>\n<h2><strong>1. Activate OAuth 2.0 for Microsoft Dynamics 365 Business Central<\/strong><\/h2>\n<p>For Dynamics 365 Business Central environments, the use of Basic Authentication (Web Service Access Key) is no longer supported, and users must switch to OAuth. I will provide a detailed walkthrough on how to configure OAuth for Dynamics 365 Business Central on-cloud.<\/p>\n<p><strong>Note:<\/strong> You can skip the steps which you have done earlier.<\/p>\n<p><strong>1.\u00a0 Create New App<\/strong> E.g. &#8220;Folio3BusinessCentralApp&#8221;<\/p>\n<ol>\n<li>Login to <strong>Azure portal<\/strong> with the same Business Central ID.<\/li>\n<li>Search \u201c<strong>App Registration<\/strong>\u201d.<\/li>\n<li>On App Registration select \u201c<strong>New registration<\/strong>\u201d.<\/li>\n<li>Enter the <strong>Name<\/strong> of the application e.g &#8220;Folio3BusinessCentralApp&#8221;.\n<ol>\n<li>Choose who can use this app or access the API. We should select a single<strong> tenant<\/strong>.<\/li>\n<li>For <strong>Redirect URL<\/strong>, choose <strong>Web<\/strong> and add the Business Central URL \u201chttps:\/\/businesscentral.dynamics.com\/\u201d.<\/li>\n<\/ol>\n<\/li>\n<li>Click on &#8220;<strong>Register<\/strong>&#8220;.<\/li>\n<\/ol>\n<p style=\"text-align: center;\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-16908\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_003.jpg\" alt=\"\" width=\"1138\" height=\"634\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_003.jpg 1138w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_003-300x167.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_003-1024x570.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_003-768x428.jpg 768w\" sizes=\"(max-width: 1138px) 100vw, 1138px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16909\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_004-1.png\" alt=\"\" width=\"1679\" height=\"886\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_004-1.png 1679w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_004-1-300x158.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_004-1-1024x540.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_004-1-768x405.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_004-1-1536x811.png 1536w\" sizes=\"(max-width: 1679px) 100vw, 1679px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-02<\/p>\n<p><strong>2. Grant API Permission<\/strong><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li>Open the new created app. So, we need to grant the API permission. Click on \u201c<strong>API permission<\/strong>\u201d on the menu and then click <strong>\u201c+ Add a permission<\/strong>\u201d.<\/li>\n<li>After clicking \u201c<strong>+ Add a permission<\/strong>\u201d, the available API permissions will come up. Select &#8220;<strong>Business Central<\/strong>&#8221; (If you can\u2019t see Business Central, it is because the user doesn\u2019t have a valid license).\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16910\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_005-1.png\" alt=\"\" width=\"1840\" height=\"793\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_005-1.png 1840w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_005-1-300x129.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_005-1-1024x441.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_005-1-768x331.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_005-1-1536x662.png 1536w\" sizes=\"(max-width: 1840px) 100vw, 1840px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-03<\/p>\n<\/li>\n<li>Click \u201c<strong>Delegated Permissions<\/strong>\u201d, check \u201c<strong>user_impersonation<\/strong>\u201d &amp; \u201c<strong>Financials.ReadWrite.All<\/strong>\u201d\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16912\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_007-1.png\" alt=\"\" width=\"932\" height=\"893\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_007-1.png 932w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_007-1-300x287.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_007-1-768x736.png 768w\" sizes=\"(max-width: 932px) 100vw, 932px\" \/><\/p>\n<\/li>\n<li>Now Click On \u201cApplication permissions\u201d.<br \/>\n(1) In Other permissions check \u201capp_access\u201d.<br \/>\n(2) In the API check \u201cAPI.ReadWrite.All\u201d.<br \/>\n(3) In Automation check \u201cAutomation.ReadWrite.All\u201d.<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16913\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_008-1.png\" alt=\"\" width=\"830\" height=\"893\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_008-1.png 830w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_008-1-279x300.png 279w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_008-1-768x826.png 768w\" sizes=\"(max-width: 830px) 100vw, 830px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-04<\/p>\n<p>After adding permissions, we will redirect back to the API permissions. All we need to do here is click on \u201c<strong>Grant admin consent for Integrated ******<\/strong>\u201c.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16914\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_009-1.png\" alt=\"\" width=\"1319\" height=\"704\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_009-1.png 1319w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_009-1-300x160.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_009-1-1024x547.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_009-1-768x410.png 768w\" sizes=\"(max-width: 1319px) 100vw, 1319px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-05<\/p>\n<p>Click on \u201c<strong>Yes<\/strong>\u201d to grant admin consent for Integrated ****.\u00a0 and Status will update and turns to green<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16915\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_010-1.png\" alt=\"\" width=\"1319\" height=\"704\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_010-1.png 1319w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_010-1-300x160.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_010-1-1024x547.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_010-1-768x410.png 768w\" sizes=\"(max-width: 1319px) 100vw, 1319px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-06<\/p>\n<p><strong>3. Create Client Secret<\/strong><\/p>\n<ol>\n<li>Next, we have to create a new client secret. Click on \u201c<strong>Certificate &amp; secret<\/strong>\u201d in the menu and click on \u201c<strong>New client secret<\/strong>\u201d. This step is important because they create the new key to pass the authentication. The key created is used as \u201cclient_secret\u201d for OAuth, which is the new way to authenticate. (remember this client_secret for later use).<\/li>\n<li>Enter client secret description e.g. \u201cFolio3BusinessCentralAuth\u201d and assign when it expires (for now select for 24 months). Click the \u201c<strong>add<\/strong>\u201d button.\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16916\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_011-1.png\" alt=\"\" width=\"1923\" height=\"892\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_011-1.png 1923w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_011-1-300x139.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_011-1-1024x475.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_011-1-768x356.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_011-1-1536x712.png 1536w\" sizes=\"(max-width: 1923px) 100vw, 1923px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-07<\/p>\n<\/li>\n<li>Copy and save the value as<strong> client secret <\/strong>as shown in the below screenshot Before the page gets refreshed or you close the page.\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16917\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_012-1.png\" alt=\"\" width=\"1292\" height=\"614\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_012-1.png 1292w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_012-1-300x143.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_012-1-1024x487.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_012-1-768x365.png 768w\" sizes=\"(max-width: 1292px) 100vw, 1292px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-08<\/p>\n<\/li>\n<li>Click on the <strong>Overview<\/strong> Note the <strong>Application (client) ID<\/strong> as <strong>client_id<\/strong> and <strong>Directory (tenant) ID<\/strong> as <strong>tenant_id<\/strong>. We need this for token generation.\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16918\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013.jpg\" alt=\"\" width=\"1287\" height=\"487\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013.jpg 1287w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-300x114.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-1024x387.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-768x291.jpg 768w\" sizes=\"(max-width: 1287px) 100vw, 1287px\" \/><\/p>\n<p>Now you have got three keys. <strong>client_secret<\/strong>, <strong>client_id<\/strong> and <strong>tenant_id<\/strong><\/li>\n<\/ol>\n<h2><strong>2. Register the Azure Active Directory in Dynamics 365 Business Central<\/strong><\/h2>\n<p>If you have Dynamics 365 BC connected to another system using (S2S) authentication that needs Client Credentials OAuth 2.0, you have to register Azure Active Directory (AAD) Applications in Business Central. This section will show you how to do that.Here D365 Automation entitlements provide access to APIs through the\u201d <strong>\/api\/microsoft\/automation<\/strong>\u201d route using OAuth client credentials flow. To access Business Central Automation APIs, you require a token with the \u201cAutomation.ReadWrite.All\u201d scope. We have added in the API permissions section.<\/p>\n<p><strong>For service-to-service authentication setup, you need to:<\/strong><\/p>\n<p>Login to <strong>Azure Portal<\/strong>, go to the App registration and select the app we created previously &#8220;Folio3BusinessCentralApp&#8221;. Click the <strong>Authentication<\/strong> on the menu and then in <strong>Web<\/strong> Redirect URI add<br \/>\n\u201chttps:\/\/businesscentral.dynamics.com\/OAuthLanding.htm&#8221;. In the end select the account and <strong>Save<\/strong>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16918\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013.jpg\" alt=\"\" width=\"1287\" height=\"487\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013.jpg 1287w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-300x114.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-1024x387.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-768x291.jpg 768w\" sizes=\"(max-width: 1287px) 100vw, 1287px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16919\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-1.png\" alt=\"\" width=\"1012\" height=\"889\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-1.png 1012w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-1-300x264.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_013-1-768x675.png 768w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-09<\/p>\n<p><strong>Now provide access permissions for that application in Business Central.<\/strong><\/p>\n<p>Note:\u00a0 You can only add a new entry in \u201c<strong>Azure Active Directory Applications\u201d <\/strong>if the logged in user is SUPER user or has \u201cSECURITY\u201d permission, the reason is that only those users will be able to Grant consent to new registration here.<\/p>\n<p>In Business Central, search \u201c<strong>AAD<\/strong>\u201d. Go to \u201c<strong>Azure Active Directory Applications<\/strong>\u201d and add a new application. Copy the <strong>client_id<\/strong> and paste it to the Client ID field in Business Central. Add Azure Portal&#8217;s app registration app name here (not mandatory to be the same but a good way for reference) in the description, the system will add the user ID and user name automatically.<\/p>\n<p>Also, add the user groups, \u201cD365 ADMINISTRATOR\u201d &amp; \u201cD365 BUS FULL ACCESS\u201d.<\/p>\n<p>Click \u201cGrant Consent\u201d and enter the Credentials username and password to accept the permission requested.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16920\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_014-1.png\" alt=\"\" width=\"1795\" height=\"889\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_014-1.png 1795w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_014-1-300x149.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_014-1-1024x507.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_014-1-768x380.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_014-1-1536x761.png 1536w\" sizes=\"(max-width: 1795px) 100vw, 1795px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-10<\/p>\n<h2>3. Call OAuth 2.0 Token API from Postman.<\/h2>\n<ol>\n<li>In Postman, create a collection and add a request named &#8220;<strong>GetToken<\/strong>&#8220;.<\/li>\n<li>Set the request method to Post and paste the URL <strong>&#8220;<\/strong>https:\/\/login.microsoftonline.com\/{tenant_id}\/oauth2\/v2.0\/token<strong>\u201d<\/strong> into the URL field. Replace <strong>{tenant_id} <\/strong>with your actual tenant ID.<\/li>\n<li>In the <strong>Body<\/strong> section, choose the <strong>&#8220;x-www-form-urlencoded&#8221;<\/strong> radio button.<\/li>\n<li>Add the following parameters:\n<ol>\n<li><strong>client_id<\/strong>: Use the client ID obtained from the Overview section as the <strong>Application (client) ID.<\/strong><\/li>\n<li><strong>client_secret<\/strong>: Use the client secret obtained in the Create Client Secret section.<\/li>\n<li><strong>scope<\/strong>: Set it to <strong>&#8220;https:\/\/api.businesscentral.dynamics.com\/.default.&#8221;<\/strong><\/li>\n<li><strong>grant_type:<\/strong> Set it to <strong>&#8220;client_credentials.&#8221;<\/strong><\/li>\n<\/ol>\n<\/li>\n<li>Click &#8220;<strong>Send<\/strong>,&#8221; and you will receive a response with a status of 200 OK, containing the token in the <strong>access_token<\/strong> key of the JSON response.<\/li>\n<\/ol>\n<p style=\"text-align: left;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16921\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_015.jpg\" alt=\"\" width=\"1384\" height=\"722\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_015.jpg 1384w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_015-300x157.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_015-1024x534.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_015-768x401.jpg 768w\" sizes=\"(max-width: 1384px) 100vw, 1384px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-11<\/p>\n<h2><strong>4. Automation API for Successful Loading and Installation of Package<\/strong><\/h2>\n<p><strong>Automation APIs Prerequisites<\/strong><\/p>\n<p><strong>Endpoints for the Automation APIs:<\/strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 https:\/\/api.businesscentral.dynamics.com\/v2.0\/<strong>{Tenant_Id}<\/strong>\/<strong>{environment_name}<\/strong>\/api\/microsoft\/automation\/v2.0\/<\/p>\n<p><strong>For Example:<\/strong> My test environment<\/p>\n<p><strong>Tenant ID (user domain name):<\/strong> 1fxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxb86<br \/>\n<strong>Environment name:<\/strong> AgriERP_QA<br \/>\n<strong>Endpoints:<\/strong> https:\/\/api.businesscentral.dynamics.com\/v2.0\/1fxxxxxx-xxxx-xxxx-xxxx- xxxxxxxxxb86\/AgriERP\/api\/microsoft\/automation\/v2.0\/<\/p>\n<p><strong>For company Id:<\/strong><\/p>\n<p>Search \u201cCompanies\u201d in Business Central, when the page is completely loaded then press Ctrl +Alt + F1, this will open business central inspect page search id, Copy the Id as Company Id.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16922\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_016.png\" alt=\"\" width=\"936\" height=\"359\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_016.png 936w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_016-300x115.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_016-768x295.png 768w\" sizes=\"(max-width: 936px) 100vw, 936px\" \/><\/p>\n<p>Now we have tenant Id, company id and environment name which we will use in automation Api\u2019s to call respective API.<br \/>\nAfter thorough research, I successfully decoded some key insights for you. Microsoft specifies the need for three requests to be sent to the automation API for the successful loading of your extension into Business Central (POST, PATCH, POST). The English names for these three requests are &#8220;Insert Extension Upload&#8221;, &#8220;Upload Extension File,&#8221; and &#8220;Install Extension.&#8221;<\/p>\n<ol>\n<li>Insert Extension Upload<\/li>\n<li>Upload Extension File<\/li>\n<li>Install Extension<\/li>\n<li>Check Extension Status<\/li>\n<\/ol>\n<p><strong>Insert Extension Upload<\/strong><\/p>\n<p>Creates an extension upload in Dynamics 365 Business Central.<\/p>\n<div class=\"table-responsive\">\n<table style=\"height: 1172px;\" width=\"1675\">\n<tbody>\n<tr>\n<td width=\"96\">Method<\/td>\n<td width=\"1220\">POST<\/td>\n<\/tr>\n<tr>\n<td width=\"96\">URL<\/td>\n<td width=\"1220\">https:\/\/api.businesscentral.dynamics.com\/v2.0\/<strong>{tenant_Id}<\/strong>\/<strong>{env_name}<\/strong>\/api\/microsoft\/automation\/v2.0\/companies(<strong>{companyId}<\/strong>)\/extensionUpload<\/td>\n<\/tr>\n<tr>\n<td width=\"96\">Authorization<\/td>\n<td width=\"1220\">Bearer {token}<\/td>\n<\/tr>\n<tr>\n<td width=\"96\">Content-Type<\/td>\n<td width=\"1220\">application\/json<\/td>\n<\/tr>\n<tr>\n<td width=\"96\">Body<\/td>\n<td width=\"1220\">{<\/p>\n<p>&#8220;schedule&#8221;:\u00a0&#8220;Current\u00a0version&#8221;,<\/p>\n<p>&#8220;schemaSyncMode&#8221;:\u00a0&#8220;Add&#8221;<\/p>\n<p>}<\/td>\n<\/tr>\n<tr>\n<td width=\"96\">Response<\/td>\n<td width=\"1220\">Http Status 200 OK<br \/>\n{\u00a0\u00a0\u00a0\u00a0&#8220;@odata.context&#8221;:\u00a0&#8220;https:\/\/api.businesscentral.dynamics.com\/v2.0\/{tenant_id}\/{env_name}\/api\/microsoft\/automation\/v2.0\/$metadata#companies({comp_id})\/extensionUpload&#8221;,&#8221;value&#8221;:\u00a0[\u00a0{&#8220;@odata.etag&#8221;:\u00a0&#8220;W\/\\&#8221;JzIwOzE1NjYyMzUyODE0NTExNjk3MDI2MTswMDsn\\&#8221;&#8221;,&#8221;systemId&#8221;:\u00a0&#8220;b1******-7184-ee11-817a-00******3948&#8243;,&#8221;schedule&#8221;:\u00a0&#8220;Current_x0020_version&#8221;,&#8221;schemaSyncMode&#8221;:\u00a0&#8220;Add&#8221;,&#8221;extensionContent@odata.mediaEditLink&#8221;:\u00a0&#8220;https:\/\/api.businesscentral.dynamics.com\/v2.0\/{tenant_id}\/AgriERP\/api\/microsoft\/automation\/v2.0\/companies({comp_id})\/extensionUpload(b1******-7184-ee11-817a-00******3948)\/extensionContent&#8221;,&#8221;extensionContent@odata.mediaReadLink&#8221;:\u00a0&#8220;https:\/\/api.businesscentral.dynamics.com\/v2.0\/{tenant_id}\/AgriERP\/api\/microsoft\/automation\/v2.0\/companies({comp_id})\/extensionUpload(b1******-7184-ee11-817a-00******3948)\/extensionContent&#8221;}]}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>The initial request (POST) notifies table 5447 that a new extension is in the process of being uploaded. Typically, this table holds just one record at a time and won\u2019t pose further concerns once all three API steps for publishing an extension are finished. Attempting to make a second POST request before completing all three steps will not be possible. The actual response from the first POST call can only be obtained by using the same API with the Get method.<\/p>\n<p>The response from the first POST Request is crucial. It includes a value called &#8220;<strong>System ID<\/strong>&#8221; which essentially serves as your &#8220;<strong>Package ID\/ExtensionUploadId<\/strong>&#8220;. Utilize this System ID\/Package ID in your subsequent PATCH request, specifically in the &#8220;<strong>extensionUploadId<\/strong>&#8221; parameter at &#8220;Upload Extension File&#8221; request. You&#8217;re now all set. You have the &#8220;<strong>extensionUploadId\/Package ID\u201d<\/strong> for the extension even before it is officially published.<\/p>\n<p><strong>Upload Extension File<\/strong><\/p>\n<p>Modifies the attributes of an extension upload entity in Dynamics 365 Business Central.<\/p>\n<div class=\"table-responsive\">\n<table width=\"0\">\n<tbody>\n<tr>\n<td width=\"72\">Method<\/td>\n<td width=\"1154\">PATCH<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">URL<\/td>\n<td width=\"1154\">https:\/\/api.businesscentral.dynamics.com\/v2.0\/<strong>{tenant_Id}<\/strong>\/<strong>{env_name}<\/strong>\/api\/microsoft\/automation\/v2.0\/companies(<strong>{companyId}<\/strong>)\/extensionUpload(<strong>{extensionUploadId}<\/strong>)\/extensionContent<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Headers<\/td>\n<td width=\"1154\">{<\/p>\n<p>&#8220;Content-Type&#8221;: &#8220;application\/octet-stream&#8221;,<\/p>\n<p>\u201cAuthorization\u201d: &#8220;Bearer $token&#8221;,<\/p>\n<p>&#8220;If-Match&#8221;: &#8220;*&#8221;,<\/p>\n<p>}<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Body<\/td>\n<td width=\"1154\">Binary Upload File<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Response<\/td>\n<td width=\"1154\">If successful, this method returns a\u00a0204 No Content.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p><strong>Install and Uninstall Published Add-on Extensions<\/strong><\/p>\n<p>Three bound actions, namely \u201c<strong>Microsoft.NAV.install<\/strong>\u201d, \u201c<strong>Microsoft.NAV.uninstall<\/strong>\u201d, and \u201c<strong>Microsoft.NAV.uninstallAndDeleteExtensionData<\/strong>\u201d are accessible on the extension\u2019s endpoint.<\/p>\n<p>After uploading the extension file, initiate the installation process by executing a POST request on the bound action \u201c<strong>Microsoft.NAV.upload<\/strong>\u201d.<\/p>\n<div class=\"table-responsive\">\n<table width=\"0\">\n<tbody>\n<tr>\n<td width=\"72\">Method<\/td>\n<td width=\"1181\">POST<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">URL<\/td>\n<td width=\"1181\">https:\/\/api.businesscentral.dynamics.com\/v2.0\/<strong>{tenant_Id}<\/strong>\/<strong>{env_name}<\/strong>\/api\/microsoft\/automation\/v2.0\/companies(<strong>{companyId}<\/strong>)\/extensionUpload(<strong>{extensionUploadId}<\/strong>)\/Microsoft.NAV.upload<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Headers<\/td>\n<td width=\"1181\">{<\/p>\n<p>\u201cAuthorization\u201d: &#8220;Bearer token&#8221;<\/p>\n<p>}<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Response<\/td>\n<td width=\"1181\">Http 200 OK<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>Uninstalling the extension can be done through the bound action\u00a0<a href=\"https:\/\/learn.microsoft.com\/en-us\/dynamics365\/business-central\/dev-itpro\/administration\/api\/dynamics_extensionupload_create\"><strong>Microsoft.NAV.uninstall<\/strong><\/a>, as with the add-on extensions. The bound action <strong>Microsoft.NAV.uninstallAndDeleteExtensionData<\/strong> can be used to delete the tables that contain data owned by the extension on uninstall. This action cannot be undone.<\/p>\n<p><strong>Check Extension Status<\/strong><\/p>\n<p>Enlist the status of deployed extensions within Dynamics 365 Business Central. The Latest one will be the recent extension uploaded.<\/p>\n<div class=\"table-responsive\">\n<table width=\"0\">\n<tbody>\n<tr>\n<td width=\"72\">Method<\/td>\n<td width=\"976\">GET<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">URL<\/td>\n<td width=\"976\">https:\/\/api.businesscentral.dynamics.com\/v2.0\/<strong>{tenant_Id}<\/strong>\/<strong>{env_name}<\/strong>\/api\/microsoft\/automation\/v2.0\/companies(<strong>{companyId}<\/strong>)\/extensionDeploymentStatus<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Headers<\/td>\n<td width=\"976\">{<\/p>\n<p>\u201cAuthorization\u201d: &#8220;Bearer token&#8221;<\/p>\n<p>}<\/td>\n<\/tr>\n<tr>\n<td width=\"72\">Response<\/td>\n<td width=\"976\">&#8220;value&#8221;:\u00a0[<\/p>\n<p>{<\/p>\n<p>&#8220;@odata.etag&#8221;:\u00a0&#8220;W\/\\&#8221;JzIwOzEyMDc1NTg2NxxxxxxxxxM2NjE4MTswMDsn\\&#8221;&#8221;,<\/p>\n<p>&#8220;operationID&#8221;:\u00a0&#8220;e3906540-8ad8-4ac6-8c7d-b104aa63f02f&#8221;,<\/p>\n<p>&#8220;name&#8221;:\u00a0&#8220;F3AgriERPServices&#8221;,<\/p>\n<p>&#8220;publisher&#8221;:\u00a0&#8220;Folio3&#8221;,<\/p>\n<p>&#8220;operationType&#8221;:\u00a0&#8220;Upload&#8221;,<\/p>\n<p>&#8220;status&#8221;:\u00a0&#8220;InProgress&#8221;,<\/p>\n<p>&#8220;schedule&#8221;:\u00a0&#8220;Immediate&#8221;,<\/p>\n<p>&#8220;appVersion&#8221;:\u00a0&#8220;2.0.1.47&#8221;,<\/p>\n<p>&#8220;startedOn&#8221;:\u00a0&#8220;2023-11-22T08:02:20.22Z&#8221;<\/p>\n<p>},<\/p>\n<p>\u2026\u2026<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>In summary these are four steps for deploying, installing and checking the status of a package.\u00a0 Next, we will set up Azure DevOps, using our organization and selecting our respective project for deployment.<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<h2><strong>5.Set up the Azure DevOps Project for CI\/CD.<\/strong><\/h2>\n<p><strong>\u00a0 \u00a0\u00a0\u00a0\u00a0<\/strong>Sign into\u00a0 <a href=\"https:\/\/aex.dev.azure.com\/\">https:\/\/aex.dev.azure.com\/<\/a> Select your organization.\u00a0 Click on your project name e.g. \u201cAgriERP **********\u201d. Your project dashboard will be loaded. Now we are going to set up our project for doing business central deployment on customer tenant environments. Basically, we have two things: Continuous Integration and Continuous Deployment. In Continuous Integration we are doing our build related work and our build is only successful when our code is compiled and build properly. For that all we need to do is to bring a compiler and Al Packages on Azure DevOps so we can easily compile and build our code.<\/p>\n<p><strong>Pre-requisites:<\/strong><br \/>\nAZURE CLI\u00a0 (Download from <a href=\"https:\/\/aka.ms\/installazurecliwindowsx64\">here<\/a>!) or Go-to Official <a href=\"https:\/\/learn.microsoft.com\/en-us\/cli\/azure\/install-azure-cli-windows?tabs=azure-cli#install-or-update\">Site<\/a>.<\/p>\n<p>I typically obtain the compiler from the local service folder <strong>&#8220;C:\\Program Files\\Microsoft Dynamics 365 Business Central\\220\\Service&#8221;<\/strong> (refer fig-31) sourced from the installed Microsoft Business Central On-Premises version. Please delete all unnecessary folders (red highlighted in Fig-32) inside the service folder and rename it as &#8220;ALCompiler.&#8221; <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16923\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_017.jpg\" alt=\"\" width=\"944\" height=\"263\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_017.jpg 944w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_017-300x84.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_017-768x214.jpg 768w\" sizes=\"(max-width: 944px) 100vw, 944px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16924\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018.jpg\" alt=\"\" width=\"1228\" height=\"949\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018.jpg 1228w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018-300x232.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018-1024x791.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018-768x594.jpg 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018-260x200.jpg 260w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_018-87x67.jpg 87w\" sizes=\"(max-width: 1228px) 100vw, 1228px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-13<\/p>\n<p>Now the ALCompiler folder looks like.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16925\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_019.jpg\" alt=\"\" width=\"350\" height=\"869\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_019.jpg 350w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_019-121x300.jpg 121w\" sizes=\"(max-width: 350px) 100vw, 350px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-14<\/p>\n<ol>\n<li>Next, go to your project directory and select the <strong>.alpackage<\/strong><\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16926\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_020.jpg\" alt=\"\" width=\"930\" height=\"441\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_020.jpg 930w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_020-300x142.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_020-768x364.jpg 768w\" sizes=\"(max-width: 930px) 100vw, 930px\" \/><\/p>\n<p style=\"text-align: center;\">Fig-15<\/p>\n<p>Now we will upload these 2 folders to our project\u2019s Artifacts. Go to your project on Azure DevOps in Menu click on <strong>Artifacts<\/strong><\/p>\n<h3><strong>\u00a01. Azure Artifacts Feed<\/strong><\/h3>\n<p>Navigate to the feeds section on the left in your Azure DevOps Project. Click on <strong>Create Feed<\/strong> button<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-16962 alignleft\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/but-ton.png\" alt=\"\" width=\"154\" height=\"53\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/but-ton.png 154w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/but-ton-150x53.png 150w\" sizes=\"(max-width: 154px) 100vw, 154px\" \/><\/p>\n<p>A panel from the left side will pop out, prompting for information about the feed. Input the <strong>Name e.g. AgriERPBC-AL_Package (<\/strong>for uploading ALCompiler folder) of the feed, <strong>Visibility<\/strong> and <strong>Scope<\/strong> and click on <strong>Create<\/strong> button<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16927\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_021.png\" alt=\"\" width=\"1908\" height=\"863\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_021.png 1908w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_021-300x136.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_021-1024x463.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_021-768x347.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_021-1536x695.png 1536w\" sizes=\"(max-width: 1908px) 100vw, 1908px\" \/><\/p>\n<p>The feed will then be available in the Artifacts section in your Azure DevOps Project.<\/p>\n<p>Next, in the Artifacts section, click on the Connect to Feed button, make sure that the feed that you just created is selected in the dropdown.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16928\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_022.png\" alt=\"\" width=\"1373\" height=\"480\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_022.png 1373w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_022-300x105.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_022-1024x358.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_022-768x268.png 768w\" sizes=\"(max-width: 1373px) 100vw, 1373px\" \/><\/p>\n<p>In the connect to feed section, scroll down and select the \u201cUniversal Packages\u201d in the Universal section, because we will be bringing our own package. On Selecting \u201cUniversal Packages\u201d a new section from the left will pop out, where you can find the PowerShell commands to publish your files to the azure feed. Go ahead and copy the \u201caz artifacts universal publish\u2026.\u201d command.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16929\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_023.png\" alt=\"\" width=\"1412\" height=\"791\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_023.png 1412w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_023-300x168.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_023-1024x574.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_023-768x430.png 768w\" sizes=\"(max-width: 1412px) 100vw, 1412px\" \/><\/p>\n<p>Open PowerShell on your local machine, and paste the \u201cazure artifacts\u201d command that you copied in the previous step.<\/p>\n<p>(Make sure you have Azure CLI installed and are logged in PowerShell with your azure account that you\u2019re using on azure DevOps portal, you can check account with \u201caz account show\u201d or login using \u201caz login\u201d)<\/p>\n<p>You can set the \u201c- -name\u201d, \u201c- -version\u201d and \u201c- -description\u201d values according to your needs, and make sure to replace the <strong>\u201c.\u201d <\/strong>in the end of the command after &#8211; -path and use the path of \u201c<strong>ALCompiler folder\u201d <\/strong>that your package folder is present at after \u2013path. The path will be to the inside of the package folder.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16930\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_024.png\" alt=\"\" width=\"1487\" height=\"527\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_024.png 1487w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_024-300x106.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_024-1024x363.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_024-768x272.png 768w\" sizes=\"(max-width: 1487px) 100vw, 1487px\" \/><\/p>\n<p>On successful publish, the output would look like this.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16931\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_025.png\" alt=\"\" width=\"1485\" height=\"432\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_025.png 1485w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_025-300x87.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_025-1024x298.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_025-768x223.png 768w\" sizes=\"(max-width: 1485px) 100vw, 1485px\" \/><\/p>\n<p>Go back to the \u201cArtifacts\u201d section on your Azure DevOps Project, and select your Feed. There, you can see the package that you uploaded in the previous step.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16932\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_026.png\" alt=\"\" width=\"906\" height=\"560\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_026.png 906w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_026-300x185.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_026-768x475.png 768w\" sizes=\"(max-width: 906px) 100vw, 906px\" \/><\/p>\n<p>Repeat the same steps of package upload to your feed, and upload the <strong>.al-packages<\/strong> folder contents of your business central project (refer to step 2 and Fig-34 above), this will be used later to build your project in the Build Pipeline (CI).<\/p>\n<h3><strong>2. <\/strong><strong>Build Pipeline<\/strong><\/h3>\n<p>In this section we will setup a build pipeline (CI). This pipeline will be responsible of building your Business Central project and generating a <strong>.app <\/strong>file, that we will deploy to BC Cloud.<\/p>\n<p>Navigate to the <strong>Pipelines <\/strong>section from the menu on the right in your Azure DevOps Project, and click on the <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16963\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/but-ton_2.png\" alt=\"\" width=\"131\" height=\"46\" \/>\u00a0\u201c<strong>New pipeline<\/strong>\u201d button.<\/p>\n<p>Here we will select the \u201cuse the classic Editor\u201d Pipeline method to create a pipeline to skip yaml configuration. In the next menu that will open, select the \u201cEmpty job\u201d.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16933\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_027.png\" alt=\"\" width=\"904\" height=\"801\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_027.png 904w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_027-300x266.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_027-768x680.png 768w\" sizes=\"(max-width: 904px) 100vw, 904px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16934\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_028.png\" alt=\"\" width=\"776\" height=\"563\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_028.png 776w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_028-300x218.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_028-768x557.png 768w\" sizes=\"(max-width: 776px) 100vw, 776px\" \/><\/p>\n<p>In this step, you will set the repository where your project code is present. The source control I am using is Bitbucket, so I will select that and choose the respective repository and branch, and click on \u201c<strong>Continue<\/strong>\u201d.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16935\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_029.png\" alt=\"\" width=\"891\" height=\"662\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_029.png 891w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_029-300x223.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_029-768x571.png 768w\" sizes=\"(max-width: 891px) 100vw, 891px\" \/><\/p>\n<p><strong>Note:<\/strong> <strong>By default, there is no connection made to any source control or repository, a service connection is required to connect to any repository. <\/strong><\/p>\n<p>A service connection can be set up by going to the <strong>Project Settings <\/strong>of your project, \u201c<strong>Service Connection<\/strong>\u201d in the Pipeline section, and selecting \u201c<strong>New Service Connection<\/strong>\u201d in the top right of the page.<\/p>\n<p>In the next menu, select the your source control where your repository exists and use your account to authenticate and create the service connection.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16936\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_030.png\" alt=\"\" width=\"1839\" height=\"729\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_030.png 1839w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_030-300x119.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_030-1024x406.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_030-768x304.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_030-1536x609.png 1536w\" sizes=\"(max-width: 1839px) 100vw, 1839px\" \/><\/p>\n<p>Inside the empty newly created build pipeline, we will leave the values for Agent job as default, and start adding tasks to it by clicking on the <strong>\u201c+\u201d button.<\/strong> In the search bar, type and select the \u201c<strong>Download Package<\/strong>\u201d task, twice since we will be using 2 packages from the feed, the \u201cPowerShell\u201d tasks, and the \u201c<strong>Publish Pipeline Artifacts<\/strong>\u201d task.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16937\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_031.png\" alt=\"\" width=\"1577\" height=\"401\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_031.png 1577w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_031-300x76.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_031-1024x260.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_031-768x195.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_031-1536x391.png 1536w\" sizes=\"(max-width: 1577px) 100vw, 1577px\" \/><\/p>\n<p>After adding the tasks, your pipeline steps would look like this. (I have renamed the \u201c<strong>Download package<\/strong>\u201d tasks)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16938\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_032.png\" alt=\"\" width=\"691\" height=\"515\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_032.png 691w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_032-300x224.png 300w\" sizes=\"(max-width: 691px) 100vw, 691px\" \/><\/p>\n<p><strong>Task 1: Download AL Package<\/strong><\/p>\n<p>Here we will enter the details of the first package that we added, the AL Compiler. The \u201cDestination Directory\u201d we set here will be referenced in the later steps, make sure to include \u201c$(System.ArtifactsDirectory)\/\u201d in the destination.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16939\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_033.png\" alt=\"\" width=\"1022\" height=\"688\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_033.png 1022w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_033-300x202.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_033-768x517.png 768w\" sizes=\"(max-width: 1022px) 100vw, 1022px\" \/><\/p>\n<p><strong>Task 2: Download AL Package Dependencies <\/strong>(for .alpackages folder)<\/p>\n<p>In this step, we will download the dependencies package from the artifacts. Similar to the previous step, we will reference the Destination directory later on.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16940\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_034.png\" alt=\"\" width=\"1017\" height=\"672\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_034.png 1017w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_034-300x198.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_034-768x507.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_034-370x245.png 370w\" sizes=\"(max-width: 1017px) 100vw, 1017px\" \/><\/p>\n<p><strong>Task 3: PowerShell Script<\/strong><\/p>\n<p>In this step we will run the command in the PowerShell task to build our project.<\/p>\n<p><strong>Command:<\/strong> $(System.ArtifactsDirectory)\/ALPackage\/alc.exe \/project:$(System.DefaultWorkingDirectory) \/out:$(Pipeline.Workspace)\/App\/F3IntegerationDev.app \/packagecachepath:$(System.ArtifactsDirectory)\/ALPackageDependencies\/<\/p>\n<p><strong>Command Breakdown<\/strong>:<\/p>\n<ul>\n<li>$(System.ArtifactsDirectory)\/ALPackage\/alc.exe:<\/li>\n<\/ul>\n<p>The executable file from where our extension artifact was downloaded in the first step<\/p>\n<ul>\n<li>\/project:$(System.DefaultWorkingDirectory):<\/li>\n<\/ul>\n<p>The location your project files are i.e your repository. The repository files can be accessed in build pipeline using the built-in azure variable $(System.DefaultWorkingDirectory)<\/p>\n<ul>\n<li>\/out:$(Pipeline.Workspace)\/App\/F3IntegerationUAT.app:<\/li>\n<\/ul>\n<p>Path where the output .app file be saved, we can temporarily save it in the pipeline workspace before we publish it to pipeline artifacts in the next step<\/p>\n<ul>\n<li>\/packagecachepath:$(System.ArtifactsDirectory)\/ALPackageDependencies\/:<\/li>\n<\/ul>\n<p>The path to the dependencies artifacts\u00a0 that we uploaded to the feed.<\/p>\n<p><strong>Task 4: Publish Pipeline Artifact<\/strong><\/p>\n<p>Now we will publish the .app file temporarily saved in the pipeline workspace to pipeline artifacts, so we can use it later in the release pipeline for deployment.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16941\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_035.png\" alt=\"\" width=\"1037\" height=\"428\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_035.png 1037w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_035-300x124.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_035-1024x423.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_035-768x317.png 768w\" sizes=\"(max-width: 1037px) 100vw, 1037px\" \/><\/p>\n<p>Create the pipeline and run it to test the steps, on success the output should look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16942\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_036.png\" alt=\"\" width=\"736\" height=\"591\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_036.png 736w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_036-300x241.png 300w\" sizes=\"(max-width: 736px) 100vw, 736px\" \/><\/p>\n<p>For more information, go inside the build details, and you can see published items.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16943\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_037.png\" alt=\"\" width=\"1275\" height=\"700\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_037.png 1275w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_037-300x165.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_037-1024x562.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_037-768x422.png 768w\" sizes=\"(max-width: 1275px) 100vw, 1275px\" \/><\/p>\n<p>In the published section, you can see the path and name of File that we published in the fourth step.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16944\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_038.png\" alt=\"\" width=\"433\" height=\"311\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_038.png 433w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_038-300x215.png 300w\" sizes=\"(max-width: 433px) 100vw, 433px\" \/><\/p>\n<h3><strong>1. <\/strong><strong>Release Pipeline<\/strong><\/h3>\n<p>In this section, we will deploy our .app file to Business Central using release pipeline (CD).<\/p>\n<p>On left main Menu click on \u201c<strong>Releases<\/strong>\u201d and click on \u201c<strong>+New<\/strong>\u201d -&gt; \u201c<strong>+New Release Pipeline<\/strong>\u201d<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16945\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_039.png\" alt=\"\" width=\"662\" height=\"600\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_039.png 662w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_039-300x272.png 300w\" sizes=\"(max-width: 662px) 100vw, 662px\" \/><\/p>\n<p>In the new release pipeline, we\u2019ll first add an artifact, which contains the .app published in the Build Pipeline. Click on <strong>Add an Artifact<\/strong>, select \u201c<strong>Build<\/strong>\u201d as type of artifact, and choose the build pipeline we just created, in the \u201c<strong>Source<\/strong>\u201d dropdown. If you have run the build pipeline to produce an artifact at least once, we would be able to see the published artifact as well in the bottom message. For the \u201c<strong>Source Alias<\/strong>&#8221; section, choose a name or leave it as default, we\u2019ll use it later in the pipeline to reference the artifact.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16946\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_040.png\" alt=\"\" width=\"1556\" height=\"817\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_040.png 1556w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_040-300x158.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_040-1024x538.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_040-768x403.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_040-1536x806.png 1536w\" sizes=\"(max-width: 1556px) 100vw, 1556px\" \/><\/p>\n<p>Now in the Pipeline,<strong> Add a stage<\/strong> and go into its tasks.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16947\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_041.png\" alt=\"\" width=\"571\" height=\"335\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_041.png 571w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_041-300x176.png 300w\" sizes=\"(max-width: 571px) 100vw, 571px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16948\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_042.png\" alt=\"\" width=\"757\" height=\"501\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_042.png 757w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_042-300x199.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_042-270x180.png 270w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_042-370x245.png 370w\" sizes=\"(max-width: 757px) 100vw, 757px\" \/><\/p>\n<p>In Navigation click <strong>Variables<\/strong> Section, and set the following variables with your respective values.<\/p>\n<ul>\n<li>app_file_name =&gt; which will be the one you used in the PowerShell script \u201c<strong>\/out:$(Pipeline.Workspace)\/App\/F3IntegerationQA.app<\/strong>\u201d of the build pipeline.<\/li>\n<li>\u201ctoken\u201d\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Leave for now, this will be populated on runtime.<\/li>\n<li>\u201cextension_id\u201d \/\/ Leave for now, we will populate them on runtime in the upcoming release tasks.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16949\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_043.png\" alt=\"\" width=\"1630\" height=\"603\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_043.png 1630w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_043-300x111.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_043-1024x379.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_043-768x284.png 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_043-1536x568.png 1536w\" sizes=\"(max-width: 1630px) 100vw, 1630px\" \/><\/p>\n<p>Now in the navigation click on <strong>Tasks<\/strong> section, we\u2019ll add 4 PowerShell tasks to the default agent job, running on \u201cWindows latest\u201d or leave it to the default agent job.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16950\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_044.png\" alt=\"\" width=\"698\" height=\"441\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_044.png 698w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_044-300x190.png 300w\" sizes=\"(max-width: 698px) 100vw, 698px\" \/><\/p>\n<h2><strong>6. PowerShell Scripts for Release Pipeline Tasks.<\/strong><\/h2>\n<p>Here, we will create PS scripts for Automation API calls. These scripts will initiate three requests to ensure the successful loading and installation of extensions into Business Central (POST, PATCH, POST). (I explained each API in <strong>Automation API for Successful Loading and Installation of Package<\/strong>).<\/p>\n<ol>\n<li><strong>PowerShell Script to Get Token<\/strong><\/li>\n<\/ol>\n<pre>$body = @{\r\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"client_id\" = \"$(client_id)\"\r\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"client_secret\" = \"$(client_secret)\"\r\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"Scope\" = <u>https:\/\/api.businesscentral.dynamics.com\/.default<\/u>\r\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"grant_type\" = \"client_credentials\"\r\n\r\n}\r\n\r\n$response = Invoke-RestMethod -Uri \"https:\/\/login.microsoftonline.com\/$(tenant_id)\/oauth2\/v2.0\/token\" -Method Post -ContentType \"application\/x-www-form-urlencoded\" -Body $body\r\n\r\n$token = $response.access_token\r\n\r\nWrite-Host \"##vso[task.setvariable variable=token]$token\"\u00a0 \/\/ Set the Azure Variable \u201c<strong>token<\/strong>\u201c value<\/pre>\n<p>The last PowerShell command \u201cWrite-Host &#8220;<strong>##vso[task.setvariable variable=token]$token<\/strong>&#8221; will populate the \u201ctoken\u201d pipeline variable with the token value from the script.<strong>PowerShell Script to Get<\/strong><\/p>\n<ol>\n<li><strong>PowerShell to Get Extension ID<\/strong><\/li>\n<\/ol>\n<pre>$UrlExtUpload = 'https:\/\/api.businesscentral.dynamics.com\/v2.0\/$(tenant_id)\/$(env_name)\/api\/microsoft\/automation\/v2.0\/companies($(company_id))\/extensionUpload'\r\n\r\ntry {\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 $headerExtensionUploadPost = @{\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Accept =\"application\/json\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Content-Type\" =\u00a0 \"application\/json\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Authorization =\"Bearer $(token)\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0}\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 $BodyExtensionUploadPost = @{\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\"schedule\"=\"Current version\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"schemaSyncMode\"=\"Add\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0} | ConvertTo-Json\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 #Calling insert extension upload API\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 $ResponseExtUploadPost = Invoke-RestMethod -Uri $UrlExtUpload -Method Post -Headers $headerExtensionUploadPost -Body $BodyExtensionUploadPost\u00a0 -ContentType \"application\/json\" -UseBasicParsing\r\n\r\n\u00a0 \u00a0 \u00a0$extension_id = $ResponseExtUploadPost.systemId\r\n\r\n# Set the variable\r\n\r\nWrite-Host \"##vso[task.setvariable variable=extension_id]$extension_id\"\u00a0 \/\/ Set value of\u00a0 \u00a0Extensionid\r\n\r\n\u00a0Write-Host \"Get Extension Id by POST: $extension_id\"\r\n\r\n\u00a0}\u00a0\r\n\r\n\u00a0catch [System.Net.WebException] {\r\n\r\n\u00a0 \u00a0 $exception = $_.Exception\r\n\r\n\u00a0 \u00a0 $response = $exception.Response\r\n\r\n\u00a0 \u00a0 if ($response -ne $null -and $response.StatusCode -eq [System.Net.HttpStatusCode]::BadRequest)\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 {\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Write-Host \"Bad Request: The server returned a 400 status code.\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Write-Host \"Calling Get ExtensionUpload API for getting extensionId.\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 $headerExtensionUploadGet = @{\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Accept =\"application\/json\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Content-Type\" =\u00a0 \"application\/json\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Authorization =\"Bearer $(token)\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 $ResponseExtensionUpload = Invoke-RestMethod -Uri $UrlExtUpload -Method Get -Headers $headerExtensionUploadGet\u00a0 -ContentType \"application\/json\" -UseBasicParsing\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 $extension_id = $ResponseExtensionUpload.value[0].systemId\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 Write-Host \"##vso[task.setvariable variable=extension_id]$extension_id\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 Write-Host \"Getting Extension Id by GET : $extension_id\"\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0} else {\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 # Handle other web-related exceptions\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 Write-Host \"An error occurred: $($exception.Message)\"\r\n\r\n\u00a0 \u00a0 \u00a0 }\r\n\r\n\u00a0}<\/pre>\n<div><\/div>\n<p>During this step, the Business Central Automation API&#8217;s POST call will provide a first time response with an HTTP Status of 200. The response packet includes a &#8220;<strong>systemId<\/strong>,&#8221; which will later serve as the &#8220;<strong>extensionUploadId<\/strong>&#8221; in subsequent API calls. To elaborate, the first initial step involves obtaining a token. The second call utilizes the token from the first call, acquiring &#8220;<strong>systemId<\/strong>&#8221; as the &#8220;<strong>extensionUploadId<\/strong>&#8221; in the initial attempt (where the <strong>extension_id <\/strong>parameter of the Release Pipeline is set). In the third step, the package is uploaded to the environment, and success is indicated by an HTTP status of 201. If this or later of any call fails, the build will also fail. Upon rerunning the pipeline, the second step will now return an HTTP status of 400 bad request, containing a response indicating that the key already exists. At this point, the same API is invoked with the <strong>GET<\/strong> method, now we will be retrieving the value of &#8220;extensionUploadId&#8221; from the &#8220;systemId&#8221; within the object at index 0 of &#8220;Value&#8221; json token.<\/p>\n<p>Please refer to the code above in the catch section:<\/p>\n<p><strong>$extension_id = $ResponseExtensionUpload.value[0].systemId.<\/strong><\/p>\n<p>The PowerShell command \u201c<strong>Write-Host &#8220;##vso[task.setvariable variable=extension_id]$extension_id<\/strong> &#8221; will populate the \u201c<strong>extension_id<\/strong>\u201d pipeline variable with the <strong>extension_id<\/strong> value from the script.<\/p>\n<ol>\n<li><strong>PowerShell Script To Upload App File<\/strong><\/li>\n<\/ol>\n<p>Below script will upload package file from Artifact to Business central tenant.<\/p>\n<pre># Now $desiredValue contains the value you want\r\n$UrlFileUpload = 'https:\/\/api.businesscentral.dynamics.com\/v2.0\/$(tenant_id)\/$(env_name)\/api\/microsoft\/automation\/v2.0\/companies($(company_id))\/extensionUpload($(extension_id))\/extensionContent'\r\n$HeaderFileUpload = @{\r\nAccept =\"*\/*\"\r\n\"Content-Type\" =\u00a0 \"application\/jsonapplication\/octet-stream\"\r\nAuthorization =\"Bearer $(token)\"\r\n\"If-Match\" = \"*\"\r\n\u00a0 }\r\n$FilePath = \"$(System.DefaultWorkingDirectory)\/{{Build_Artifact_Name}}\/AppFile\/$(app_file_name)\"\r\n$response = Invoke-RestMethod -Uri $UrlFileUpload -Method Patch -Headers $HeaderFileUpload -ContentType \"application\/octet-stream\" -InFile $FilePath \u2013UseBasicParsing<\/pre>\n<ol>\n<li><strong>PowerShell Script to Install App<\/strong><\/li>\n<\/ol>\n<pre># Write your PowerShell commands here.\r\n$UrlFileInstall = 'https:\/\/api.businesscentral.dynamics.com\/v2.0\/$(tenant_id)\/$(env_name)\/api\/microsoft\/automation\/v2.0\/companies($(company_id))\/extensionUpload($(extension_id))\/Microsoft.NAV.upload'\r\n$HeaderFileInstall = @{\r\nAccept =\"*\/*\"\r\nAuthorization =\"Bearer $(token)\"\r\n\u00a0 }\r\nInvoke-RestMethod -Uri $UrlFileInstall -Method Post -Headers $HeaderFileInstall \u2013UseBasicParsing<\/pre>\n<p>Above script will use the uploaded package and install it in the environment. After all the steps are set, we will create a release for the deployment to Business Central.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16951\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_045.png\" alt=\"\" width=\"1048\" height=\"584\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_045.png 1048w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_045-300x167.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_045-1024x571.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_045-768x428.png 768w\" sizes=\"(max-width: 1048px) 100vw, 1048px\" \/><\/p>\n<h3><strong>Status Check of Installation.<\/strong><\/h3>\n<p>For Status check of the app file installation on Business Central, a separate pipeline can be setup, that will give status on every run.<\/p>\n<p>Create a new release pipeline like the previous one, but no artifact is required to be added since this will just be used for status check.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16952\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_046.png\" alt=\"\" width=\"752\" height=\"488\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_046.png 752w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_046-300x195.png 300w\" sizes=\"(max-width: 752px) 100vw, 752px\" \/><\/p>\n<p>In the variables section, following variables will be added. The token variable will be left empty here as well and populated on runtime.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16953\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_047.png\" alt=\"\" width=\"1217\" height=\"437\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_047.png 1217w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_047-300x108.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_047-1024x368.png 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_047-768x276.png 768w\" sizes=\"(max-width: 1217px) 100vw, 1217px\" \/><\/p>\n<p>In the tasks section, add 2 PowerShell tasks, one to fetch the auth token (similar as previous release pipeline) and one to fetch installation status.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16954\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_048.png\" alt=\"\" width=\"701\" height=\"305\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_048.png 701w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_048-300x131.png 300w\" sizes=\"(max-width: 701px) 100vw, 701px\" \/><\/p>\n<p><strong>PowerShell Script to Get Token<\/strong> task will be the same as before.<\/p>\n<p><strong>PowerShell Script to check Status:<\/strong><\/p>\n<pre># Write your PowerShell commands here.\r\n$UrlFileInstall = 'https:\/\/api.businesscentral.dynamics.com\/v2.0\/$(tenant_id)\/$(env_name)\/api\/microsoft\/automation\/v2.0\/companies($(company_id))\/extensionUpload($(extension_id))\/Microsoft.NAV.upload'\r\n$HeaderFileInstall = @{\r\nAccept =\"*\/*\"\r\nAuthorization =\"Bearer $(token)\"\u00a0 }\r\nInvoke-RestMethod -Uri $UrlFileInstall -Method Post -Headers $HeaderFileInstall \u2013UseBasicParsing\r\n\r\n\r\necho $response.value[0].status\r\n\r\n<\/pre>\n<p>This will return a status of the installation, like so in line 12: \u201cFailed\u201d<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16955\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_049.png\" alt=\"\" width=\"993\" height=\"370\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_049.png 993w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_049-300x112.png 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_049-768x286.png 768w\" sizes=\"(max-width: 993px) 100vw, 993px\" \/><\/p>\n<p>Here our release pipeline is executed successfully, the failed status is due to deploying the extension with the same version and I did this to just test the deployment successfully.\u00a0 Refer below screenshots.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16956\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_050.jpg\" alt=\"\" width=\"1763\" height=\"311\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_050.jpg 1763w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_050-300x53.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_050-1024x181.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_050-768x135.jpg 768w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_050-1536x271.jpg 1536w\" sizes=\"(max-width: 1763px) 100vw, 1763px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-16957\" src=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_051.jpg\" alt=\"\" width=\"1215\" height=\"613\" srcset=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_051.jpg 1215w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_051-300x151.jpg 300w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_051-1024x517.jpg 1024w, https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/screenshot_051-768x387.jpg 768w\" sizes=\"(max-width: 1215px) 100vw, 1215px\" \/><\/p>\n<p>Don\u2019t get confused by the time difference in PowerShell script execution and environment, its UTC difference, enjoy successful execution of deployment packages using Azure DevOps CI\/CD. If you have any query, reach out to us at <a href=\"mailto:dynamics@folio3.com\">Folio3 Dynamics<\/a> or <a href=\"mailto:qaisarnisar@folio3.com\">me<\/a>!<\/p>\n<p><strong>Thank You!<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Business Central Manual Package Deployment Microsoft has developed Business Central as an Enterprise Resource Planning (ERP) system. When we mention a Software as a Service (SaaS) deployment of Business Central, we are referring to a cloud-based deployment model. In this model, Microsoft hosts and manages the software on their servers, and users access it over [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":17015,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[37],"tags":[140],"class_list":["post-16906","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-business-central","tag-business-central"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD\" \/>\n<meta property=\"og:description\" content=\"Business Central Manual Package Deployment Microsoft has developed Business Central as an Enterprise Resource Planning (ERP) system. When we mention a Software as a Service (SaaS) deployment of Business Central, we are referring to a cloud-based deployment model. In this model, Microsoft hosts and manages the software on their servers, and users access it over [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\" \/>\n<meta property=\"og:site_name\" content=\"Folio3 Dynamics Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-12-28T08:32:58+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-01T08:09:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"900\" \/>\n\t<meta property=\"og:image:height\" content=\"675\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Folio3 Noc\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Qaisar Nisar &amp; Syed Ahmed Hashir\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"28 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\"},\"author\":{\"name\":\"Folio3 Noc\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/person\/525af9980ae81d25f9b7c032047fb0cc\"},\"headline\":\"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD\",\"datePublished\":\"2023-12-28T08:32:58+00:00\",\"dateModified\":\"2024-01-01T08:09:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\"},\"wordCount\":4021,\"publisher\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg\",\"keywords\":[\"business central\"],\"articleSection\":[\"Business Central\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\",\"url\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\",\"name\":\"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD\",\"isPartOf\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg\",\"datePublished\":\"2023-12-28T08:32:58+00:00\",\"dateModified\":\"2024-01-01T08:09:13+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage\",\"url\":\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg\",\"contentUrl\":\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg\",\"width\":900,\"height\":675},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Blog\",\"item\":\"https:\/\/dynamics.folio3.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#website\",\"url\":\"https:\/\/dynamics.folio3.com\/blog\/\",\"name\":\"Folio3 Dynamics Blog\",\"description\":\"News, Guides and Info from the world of Microsoft Dynamics\",\"publisher\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/dynamics.folio3.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#organization\",\"name\":\"Folio3\",\"url\":\"https:\/\/dynamics.folio3.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2022\/01\/footer-logo.png\",\"contentUrl\":\"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2022\/01\/footer-logo.png\",\"width\":154,\"height\":100,\"caption\":\"Folio3\"},\"image\":{\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/person\/525af9980ae81d25f9b7c032047fb0cc\",\"name\":\"Folio3 Noc\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/61332f836e347f0f3df09f2102d55947?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/61332f836e347f0f3df09f2102d55947?s=96&d=mm&r=g\",\"caption\":\"Folio3 Noc\"},\"url\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/","og_locale":"en_US","og_type":"article","og_title":"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD","og_description":"Business Central Manual Package Deployment Microsoft has developed Business Central as an Enterprise Resource Planning (ERP) system. When we mention a Software as a Service (SaaS) deployment of Business Central, we are referring to a cloud-based deployment model. In this model, Microsoft hosts and manages the software on their servers, and users access it over [&hellip;]","og_url":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/","og_site_name":"Folio3 Dynamics Blog","article_published_time":"2023-12-28T08:32:58+00:00","article_modified_time":"2024-01-01T08:09:13+00:00","og_image":[{"width":900,"height":675,"url":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg","type":"image\/jpeg"}],"author":"Folio3 Noc","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Qaisar Nisar &amp; Syed Ahmed Hashir","Est. reading time":"28 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#article","isPartOf":{"@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/"},"author":{"name":"Folio3 Noc","@id":"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/person\/525af9980ae81d25f9b7c032047fb0cc"},"headline":"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD","datePublished":"2023-12-28T08:32:58+00:00","dateModified":"2024-01-01T08:09:13+00:00","mainEntityOfPage":{"@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/"},"wordCount":4021,"publisher":{"@id":"https:\/\/dynamics.folio3.com\/blog\/#organization"},"image":{"@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage"},"thumbnailUrl":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg","keywords":["business central"],"articleSection":["Business Central"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/","url":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/","name":"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD","isPartOf":{"@id":"https:\/\/dynamics.folio3.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage"},"image":{"@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage"},"thumbnailUrl":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg","datePublished":"2023-12-28T08:32:58+00:00","dateModified":"2024-01-01T08:09:13+00:00","breadcrumb":{"@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#primaryimage","url":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg","contentUrl":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2023\/12\/networking-with-four-laptops-internet-connection-cloud-data-storage-server-room_39422-814.jpg","width":900,"height":675},{"@type":"BreadcrumbList","@id":"https:\/\/dynamics.folio3.com\/blog\/microsoft-dynamics-365-business-central-deployment-using-azure-devops-ci-cd\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Blog","item":"https:\/\/dynamics.folio3.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Microsoft Dynamics 365 Business Central Deployment using Azure DevOps CI\/CD"}]},{"@type":"WebSite","@id":"https:\/\/dynamics.folio3.com\/blog\/#website","url":"https:\/\/dynamics.folio3.com\/blog\/","name":"Folio3 Dynamics Blog","description":"News, Guides and Info from the world of Microsoft Dynamics","publisher":{"@id":"https:\/\/dynamics.folio3.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/dynamics.folio3.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/dynamics.folio3.com\/blog\/#organization","name":"Folio3","url":"https:\/\/dynamics.folio3.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2022\/01\/footer-logo.png","contentUrl":"https:\/\/dynamics.folio3.com\/blog\/wp-content\/uploads\/2022\/01\/footer-logo.png","width":154,"height":100,"caption":"Folio3"},"image":{"@id":"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/person\/525af9980ae81d25f9b7c032047fb0cc","name":"Folio3 Noc","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dynamics.folio3.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/61332f836e347f0f3df09f2102d55947?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/61332f836e347f0f3df09f2102d55947?s=96&d=mm&r=g","caption":"Folio3 Noc"},"url":""}]}},"_links":{"self":[{"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/posts\/16906","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/comments?post=16906"}],"version-history":[{"count":0,"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/posts\/16906\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/media\/17015"}],"wp:attachment":[{"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/media?parent=16906"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/categories?post=16906"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dynamics.folio3.com\/blog\/wp-json\/wp\/v2\/tags?post=16906"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}