Azure Logic Apps – Simple, easy method to deploy a Logic App (for basic ones) using Azure PowerShell / Azure Bicep

Welcome!

This post is very juicy, very interesting, and hopefully very valuable to coders that are in need for a solution on how to deploy a Logic App. When I mean ‘deploy’, I essentially mean the ability to deploy a Logic App that is currently on a ‘Dev’ subscription/resource group, onto a ‘Prod’ subscription/resource group.

Admittingly, this article is very direct and doesn’t provide a lot of background, so you may want to learn more about ‘Azure PowerShell’ and ‘Azure Bicep’ in addition to simply reading over the code I have placed below.

For this article though, I’m going to go ahead and show the code first and then explain what it does afterwards. NOTE: Be aware that you do need to create ‘Parameters’ for your Logic App within Azure Portal if you plan on setting those to your ‘Prod’ Logic App

NOTE: The below code applies to a ‘Consumption’ Logic App. A ‘Standard’ Logic App is probably similar, but there will probably be differences between what’s written below and what you will actually need for your code

So thus, without further ado, the code for deploying a Logic App (using PowerShell and Azure Bicep is)

DeployMyLogicApp.ps1


<# NOTE: Before running this script, you must first run a 'Connect-AzAccount'  cmd in PowerShell #>

$LogicAppDevName = 'logic-mylogicapp-dev-001'
$ResourceGroupDevName = 'rg-mydevresourcegroup-001'

$LogicAppProdName = 'logic-mylogicapp-prod-001'
$ResourceGroupProdName = 'rg-myprodresourcegroup-001'

Set-AzContext -SubscriptionId 'my-prod-subscription-id'

$LogicAppProd = Get-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -ErrorAction SilentlyContinue

if ($LogicAppProd -eq $null) {
	# NOTE: You don't have to use the Location "Central US"; Use the location that you want to use
	New-AzTenantDeployment -Location "Central US" -TemplateFile ./initialDeployMyLogicApp_Main.bicep
}
else {
	
	Set-AzContext -SubscriptionId 'my-dev-subscription-id'
	
	$LogicAppDev = Get-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -ErrorAction SilentlyContinue
	
	Set-AzContext -SubscriptionId 'my-prod-subscription-id'
	
	Set-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -Definition $LogicAppDev.Definition -Force
}

$LogicAppProd = Get-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -ErrorAction SilentlyContinue

$LogicAppProd.Definition.parameters.DeploymentEnvironment.defaultValue = 'PROD' # NOTE This parameter does not have a functional purpose; its more of a 'tag'
$LogicAppProd.Definition.parameters.MyParameter1.defaultValue = 'MyParameter1Value'
$LogicAppProd.Definition.parameters.MyParameter2.defaultValue = 'MyParameter2Value'
# ...etc, for all parameters...

Set-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -Definition $LogicAppProd.Definition -Force

<# API Connections #>

# Remember to set the API Connections for your deployed (ie Production) Logic App after your first deploy.  This can be done within the Azure Portal, and only needs to be done after the first deploy

 

initialDeployMyLogicApp_Main.bicep



targetScope = 'tenant'

module getLogicAppDevModule 'initialDeployMyLogicApp_Part1.bicep' = {
	name: 'initialDeployMyLogicAppPart1'
	scope: resourceGroup('my-dev-subscription-id','rg-mydevresourcegroup-001')
}

module createLogicAppProdModule 'initialDeployMyLogicApp_Part2.bicep' = {
	name: 'initialDeployMyLogicAppPart2'
	scope: resourceGroup('my-prod-subscription-id','rg-myprodresourcegroup-001')
	params: {
		logicAppProperties: getLogicAppDevModule.outputs.logicAppProperties
	}
}

 

initialDeployMyLogicApp_Part1.bicep




resource logicAppDev 'Microsoft.Logic/workflows@2019-05-01' existing = {
	name: 'logic-mylogicapp-dev-001'
}

output logicAppProperties object = logicAppDev.properties


 

initialDeployMyLogicApp_Part2.bicep




param logicAppProperties object

resource logicAppProd 'Microsoft.Logic/workflows@2019-05-01' = {
	name: 'logic-mylogicapp-prod-001'
	location: resourceGroup(().location
	properties: logicAppProperties
}

 

Okay, that’s a pretty good chunk of code to digest, so I’ll explain it a little bit below.

DeployMyLogicApp.ps1

After setting all the constants at the top for LogicAppName/ResourceGroup name, the script will first set the subscription to Prod (Set-AzContext -SubscriptionId ‘my-prod-subscription-id’) and once we have that context, the script will now load the Prod Logic App ($LogicAppProd = Get-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -ErrorAction SilentlyContinue). Once it attempts to load it (and store in a variable), we then do a check on that variable (if ($LogicAppProd -eq $null)) to see if it is null.

If it is, then it indicates that the Logic App has never been deployed before and that we must deploy it for the first time. We will proceed with doing that (New-AzTenantDeployment -Location “Central US” -TemplateFile ./initialDeployMyLogicApp_Main.bicep). This will then run the Azure Bicep templates and create our Logic App (see sections below).

If is is not null, then it indicates that our Logic App has been deployed before, and thus we only need to update the existing Logic App. The following lines accomplish this (Set-AzContext -SubscriptionId ‘my-dev-subscription-id’

$LogicAppDev = Get-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -ErrorAction SilentlyContinue

Set-AzContext -SubscriptionId ‘my-prod-subscription-id’

Set-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -Definition $LogicAppDev.Definition -Force). This section of code fetches the Logic App that is currently in ‘Dev’, and then sets its ‘Definition’ to that of the ‘Prod’ Logic App.

After we have done the above, we now continue to the following section:

$LogicAppProd = Get-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -ErrorAction SilentlyContinue

$LogicAppProd.Definition.parameters.DeploymentEnvironment.defaultValue = ‘PROD’ # NOTE This parameter does not have a functional purpose; its more of a ‘tag’
$LogicAppProd.Definition.parameters.MyParameter1.defaultValue = ‘MyParameter1Value’
$LogicAppProd.Definition.parameters.MyParameter2.defaultValue = ‘MyParameter2Value’
# …etc, for all parameters…

Set-AzLogicApp -Name $LogicAppProdName -ResourceGroupName $ResourceGroupProdName -Definition $LogicAppProd.Definition -Force

These lines of code are for setting any ‘PROD’ parameters that you would like to setup for your ‘PROD’ Logic App. To do this, you must first create these Parameters in Azure Portal (go to “Logic app designer” > “Parameters”….and then create your Parameters here). Once you have these Parameters created, you can then set them once you deploy to ‘PROD’ with the values that you write into your code, like shown above.

This now finishes the DeployMyLogicApp.ps1 script

initialDeployMyLogicApp_Main.bicep

First off, note that we are doing a targetScope = ‘tenant’ deployment. This means our templates are being run at the ‘tenant’ level, so you have to make sure that a) the current tenant is the one you want to be using, and b) the tenant has access to all of Prod Subscription/Prod Resource Group/Dev Subscription/Dev Resource Group. If you need more information about this, google ‘Azure Bicep tenant scope’.

As for the actual working code itself, the Main template calls two sub-templates (or ‘modules’ I should say). The first one fetches the ‘Dev’ Logic App, and the second one takes the ‘Dev’ Logic App properties (or really, the definition of the Dev Logic App) as an input parameter and creates the ‘Prod’ Logic App, setting the properties of the ‘Prod’ Logic App to that of the ‘Dev’ Logic App.

And that concludes the initialDeployMyLogicApp_Main.bicep template

initialDeployMyLogicApp_Part1.bicep

Fairly simple and straight forward – fetches the existing ‘Dev’ Logic App, and outputs its properties

initialDeployMyLogicApp_Part2.bicep

Also fairly simple – takes the ‘logicAppProperties’ as an input parameter (this is the properties of the ‘Dev’ Logic App that is passed in, from the initialDeployMyLogicApp_Main.bicep template) and creates a new Logic App, in our Prod subscription/resource group (see the ‘scope’ property in the module call in the initialDeployMyLogicApp_Main.bicep template). This new Logic App is also defined with the properties of the ‘Dev’ Logic App > this will essentially give the ‘Prod’ Logic App the same code as the ‘Dev’ Logic App.

That concludes the coding part.

Now, one last key note I should make – these scripts above will ‘not’ work if you have a fairly complex Logic App (ie the above scripts are not able to deploy a Logic App that depends on/contains an Azure Function in it). If you are trying to do this, it is a ‘LOT’ harder! But it is doable – I will hopefully get around to making a post of it later. I will leave you with this though > you can do it if you manually deploy the prod Azure Function (which you only have to do for the initial deploy) and then in your script utilize the azcopy copy “sourceUrlForStorageOfTheSourcFuncApp” “destUrlForStorageOfDestFuncApp” –recursive and this will update the Prod Azure Function App with the definition of your Dev Azure Function App ‘Function’ call (one individual function anyways). Hopefully that will put you in the right direction 🙂

And that concludes this tutorial! Very useful for anyone trying to deploy Logic Apps with scripts/bicep templates 🙂 Hope this works out well for you during your coding travels!

Cheers,

Jeff

Azure Logic Apps – The ‘try/catch’ equivalent in Logic Apps

Hi there 🙂

For many programmers, the concept of a try/catch block is very familiar – you have a section of code that executes (the ‘try’ block), and if within that section an ‘exception’ occurs, the ‘catch’ block will then execute.

Fortunately, Logic Apps does have an exact equivalent for this 🙂 I will place it below.

(FYI – I did steal part of what’s below from another post I just made, but it applies to this post as well so I’m borrowing it 🙂 )

To implement a try/catch equivalent in Logic Apps, you can do the following

1) Add a ‘Scope’ block
2) Now that a ‘Scope’ block is in place, whenever an ‘exception’, or more specifically, an action or block ‘fails’ within the Scope block, you can handle that ‘exception’ (or ‘fail’) by adding another step after the ‘Scope’ block. You can now click on the ellipses located to the right of the title of the newly added block, and select the ‘Configure run after’ option, choose the ‘has failed’ option, and now, this newly added block will run whenever the ‘Scope’ block fails.
3) Note that you can also add another action after the ‘Scope’ block and you can set the ‘Configure run after’ to ‘has succeeded’, and then this block will execute when the ‘Scope’ block does not throw an exception (ie does not fail)

And there you have it! A Scope block, a block that runs after the Scope ‘has failed’, and thus basically the same building blocks that a try/catch block would do 🙂

Hope this helps!

Jeff

Azure Logic Apps – How to ‘throw an exception’ in Logic Apps

Hello again 🙂

While I was working away on a Logic App, I came across a situation where I wanted to essentially ‘throw an exception’, and was wondering what the equivalent option in Logic Apps is.

There are two ways you can ‘throw an exception’ in Logic Apps:

1) You can use a ‘Terminate’ action, assuming you want to end your Logic App completely (it will halt execution of the Logic App, and immediately cause the Logic App to ‘Fail’)

or

2) If you do not want to terminate the Logic App entirely, you can

a) Add a ‘Scope’ block
b) Simulate an ‘exception’ by adding a block inside the ‘Scope’ block that intentionally fails (ie add a ‘Parse JSON’ action where the ‘Content’ does not match the with ‘Schema’ defined, and thus causes the action to fail due to the ‘Content’ not being able to parse properly)
c) You can then handle the ‘exception’ accordingly by using the ‘Configure run after’ option > Add another step after the ‘Scope’ block, and click on the ellipses located to the right of the title of the newly added block, and select the ‘Configure run after’ option. Then select the ‘has failed’ option. Now, the block you have just created will run whenever the ‘Scope’ block fails.

Both of these options allow for a decent way to add exception handling to your Logic App 🙂

Hope this helps! Also, feel free to add comments to this post if needed.

Cheers,

Jeff

Azure Logic Apps – how to use a ‘local’ variable in a loop (‘For each’, ‘Until’) with parallel execution

Hi there,

While working with Azure Logic Apps, I was faced with a difficult challenging of coming up with a ‘local’ variable (actually, it was just a loop counter really) for a ‘For each’ loop.

I immediately went to using an ‘Initialize Variable’, setting the value to zero, and in my ‘For each’ loop simply do an ‘Increment Variable’ to increment it on each iteration.

What I didn’t realize at first – it was incremented by each running loop! Loops run in parallel in Logic Apps (at least by default – you can go to ‘Settings’ within the For Each block itself and set the ‘degree of parellism’ to 1, and this will also solve the problem besides the solution I’m writing), and because they run in parallel, each iteration increments the same Variable! I sort of imagined that each iteration would have its own local variable, but this is not the case.

So….unless you do the solution of setting the ‘degree of parellism’ to 1, you will need to do something to handle this. In my case, it was okay to set the ‘degree of parellism’ to 1 (the app I wrote was just doing routine maintenance, so it didn’t matter that it took an extra 5 min), but I did think of a possible solution on how you could handle the parallel case:

It’s a little bit heavy, because you will need either a) an Azure SQL DB, or b) Azure Storage, but a possible solution is:

– Create a temp table in an Azure SQL DB that will store the local variable for each iteration (where, the ‘primary key’ to your table could either be an ID you have handy, or maybe you can inject a ‘row index’ value into the item your iterating over beforehand ie before your ‘for each’ loop)

or

– Create temp files in the Azure storage, with each file associated with each iteration of your loop, and this file could store the local variable

Admittingly, neither solution is really that pretty or elegant, but I’m pretty certain it would work. It’s also dependant on having either an Azure SQL DB or a Azure storage. If neither of these is available, you might be able to use to some other ‘storage’ medium to accomplish the same goal (if its available).

Also, although not as powerful as the solution above, you may want to consider using the code function iterationIndexes(‘loopName’) as a loop counter if that’s all you need. I think it only works for ‘Until’ loops though, not for ‘For each’ loops. Feel free to google it to learn more.

Well, not the prettiest solution, but hopefully a ‘solution’ nonetheless 🙂 Hopefully this tip will help you.

Cheers!

Jeff

Azure Logic Apps – commit Logic App to Git (or any other) repo

SOLUTION FOR COMMITTING A LOGIC APP TO A (GIT) REPO:

In my recent travels as a programmer, I have been tasked with creating a Logic App for the company I work for, and one of the things I was asked was to be able to ‘commit’ the Logic App to a Git repo.

The answer didn’t come to me immediately, and I did do some research on the web to try and find a solution, but once I read an article about ‘blob storage’ and ‘json’ files…..I then remembered that a Logic App is essentially defined completely as a JSON file! Thus, I then came to this solution for committing a Logic App to Git repo:

1) Log into Azure Portal
2) Select the Logic app code view option on the left nav bar
3) Copy/Paste all the code you see in the UI into a JSON file (and save this file on your local)
4) Commit the file to Git 🙂

And that’s about it! Hopefully this tip will help you in your coding travels.

Cheers,

Jeff

Telerik – Appbuilder > Getting javascript / jquery scroll functions to work in Telerik

/***********************************************

UPDATE

There is now a new way for scrolling to the top of the page in Telerik, and it does NOT require you to set the Kendo application to have native scrolling enabled!

The function call for doing this is:


kendo.mobile.application.scroller().reset();

Calling this function will scroll the screen to the top. And it does not require you to have to set useNativeScrolling: true as well.

In fact, I would recommend the above solution BEFORE trying the solution below, as I have had issues with the below solution (the application would lock-up the scrolling, so the user could not even scroll the window at all, ie with a finger swipe).

Hope this helps!

Jeff

***********************************************/

Hi there!

The other day I needed to find a way to scroll to the top of the page in Telerik (after pressing a button). I tried a standard javascript function call to do this – namely, window.scrollTo(0, 0), but this didn’t work for me right away. I had to set the Kendo application object to have native scrolling enabled in order to get this function to work.

To do this, find in your code where you have declared the kendo.mobile.Application object, and modify its initialization properties to have useNativeScrolling: true. In my code (generated by Telerik), this is found in the app.js file:

app.js


var bootstrap = function() {
        $(function() {
            app.mobileApp = new kendo.mobile.Application(document.body, {
                transition: 'slide',
                skin: 'nova',
                initial: 'components/home/view.html',
                useNativeScrolling: true
            });

            kendo.bind($('.navigation-link-text'), app.navigation.viewModel);
        });
    };

By default, the useNativeScrolling: true line is not included, so you have to add that in. Once that line is added, you can now use window.scrollTo(0, 0) or other javascript/jquery scroll functions in your code!

By the way, if you do not want to use native scrolling, you may want to consider using the Kendo Mobile ‘scroller’ object instead. I found myself that this would not work for me, but it might work for you. You can find out more information about it from here – http://docs.telerik.com/kendo-ui/api/javascript/mobile/ui/scroller. Also, if you want extra Kendo tips, as well as more info about the differences/advantages of native scrolling (vs Telerik scrolling) you can visit this link here – http://developer.telerik.com/featured/20-kendo-ui-mobile-telerik-appbuilder-tips-tricks/.

Hope this helps!

Telerik – Appbuilder > Enable / Disable navigation (in a view)

Hi there!

A new lesson in coding I had to learn the other day – I was working with Telerik – Appbuilder, and I wanted to be able to add the navigation to a view (the bottom menu) but at the same time did NOT want to add the current view to the navigation. With the Telerik UI, you can add the navigation to a view, but it always requires that the current view also be included in it.

So, I found a way to add the navigation, without adding the current view to it! The below code does it, and works as follows:

1) First, you will need to figure out the id of the current view. Open the view.html file of the view you want to add the navigation to, and look at the very top line. It will look something like this:

view.html


<div data-role="view" data-title="View" data-layout="main-nonav" data-model="app.myView" data-show="app.myView.onShow" data-after-show="app.myView.afterShow" data-reload="true" id="myViewScreen" class="screen">

If you look carefully, you will see the id of this view is myViewScreen.

Also note that in this view, you can see that the data-layout attribute is set to main-nonav. As you could imagine, this means the view is configured to have no navigation! To fix this, the below code does this, and is as follows:

view.html


<!-- START_CUSTOM_CODE_myView -->
    <!-- Add custom code here. For more information about custom code, see http://docs.telerik.com/platform/screenbuilder/troubleshooting/how-to-keep-custom-code-changes -->
    <script>
        // enable navigation
        $('#myViewScreen').attr("data-layout", "main");
    </script>
    .... (rest of HTML/code for view)

This little chunk of code simply changes the attribute value of the view (the attribute being ‘data-layout’) to ‘main’. And now that the view has this value for the ‘data-layout’ attribute, it will now have the navigation in it!

NOTE: One big thing to note as well about the above code – you may be tempted to simply change the ‘data-layout’ attribute in the very first line of view.html to ‘main’. YOU DO NOT WANT TO DO THIS! And the reason you do not want to do this is because, the first line of code is Telerik-generated. This means, once you make changes to the views (ie add a new view, or change a view title), it will erase the data-layout=”main” change, and you will no longer have the desired functionality.

Hope this helps!

PS. Also, although I haven’t tried it, I could imagine you could disable the nav from a view, for a view that you want to include in the navigation. In other words, you want the current view to be in the nav bar, but you don’t want the nav bar to show in the current view.

If this is what you want to do, you could imagine it would be similar to the above. Simply find the id (as discussed above) of the view, and with it, add the following code to view.html:

view.html


<!-- START_CUSTOM_CODE_myView -->
    <!-- Add custom code here. For more information about custom code, see http://docs.telerik.com/platform/screenbuilder/troubleshooting/how-to-keep-custom-code-changes -->
    <script>
        // disable navigation
        $('#myViewScreen').attr("data-layout", "main-nonav");
    </script>
    .... (rest of HTML/code for view)

As you can see, this will change the ‘data-layout’ attribute value from “main” (which it should already be) to “main-nonav”. By doing this, it should remove the nav from the current view.

Again, hope this helps 😉

Happy coding everyone!

Entity Framework 7 > Seed a user and setting up roles

During my coding travels the other day, I was combing the web in search for a tutorial on how to add an initial user (via migrations) and setup a role for that user in EF7.  Being unable to find such a post, I have decided to post my own as a tutorial.

SETTING UP AN ‘ADMIN’ USER AND ‘ADMIN’ ROLE

I would have imagined that to set this up, the code should go somewhere in the ‘Migrations’ code files.  Ironically enough, this didn’t seem to work for me.  Hopefully in the future this sort of thing will go in ‘Migrations’, but at this point in time, I turned elsewhere to ‘Models/ApplicationDbContext.cs’ and ‘Startup.cs’.  When ‘Startup.cs’ is executed upon project startup, it can check for seed users/roles.  And DB accessibility is available via ‘ApplicationDbContext.cs’

As for the code that does this magic, it is as follows:

Startup.cs


...
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseApplicationInsightsRequestTelemetry();

            if (env.IsDevelopment())
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();

                try
                {
                    using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
                        .CreateScope())
                    {
                        serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
                             .Database.Migrate();

                        var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
                        var roleManager = app.ApplicationServices.GetService<RoleManager<IdentityRole>>();

                        serviceScope.ServiceProvider.GetService<ApplicationDbContext>().EnsureSeedData(userManager, roleManager);
                    }
                }
                catch { }
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");

                // For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859
                try
                {
                    using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
                        .CreateScope())
                    {
                        serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
                             .Database.Migrate();

                        var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
                        var roleManager = app.ApplicationServices.GetService<RoleManager<IdentityRole>>();

                        serviceScope.ServiceProvider.GetService<ApplicationDbContext>().EnsureSeedData(userManager, roleManager);
                    }
                }
                catch { }
            }
...

 

ApplicationDbContext.cs


public class ApplicationDbContext : IdentityDbContext
    {
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);

        }

        public async void EnsureSeedData(UserManager userMgr,RoleManager roleMgr)
        {
            if (!this.Users.Any(u => u.UserName == "admin@mydomain.com"))
            {
                // Add 'admin' role
                var adminRole = await roleMgr.FindByNameAsync("admin");
                if (adminRole == null)
                {
                    adminRole = new IdentityRole("admin");
                    await roleMgr.CreateAsync(adminRole);
                }

                // create admin user
                var adminUser = new ApplicationUser();
                adminUser.UserName = "admin@mydomain.com";
                adminUser.Email = "admin@mydomain.com";

                await userMgr.CreateAsync(adminUser, "MYP@55word");

                await userMgr.SetLockoutEnabledAsync(adminUser, false);
                await userMgr.AddToRoleAsync(adminUser, "admin");
            }
        }
...

NOTES

  • If you find your seed user is not being added, it could be due to your password not meeting password restriction requirements.  To check, change the line to
    var result = await userMgr.CreateAsync(adminUser, "<YOURPASSWORDHERE>");

    and view the result variable in the debugger to see what requirements you are missing

  • In Startup.cs, you need to place repeating code in both the
    if (env.IsDevelopment())

    and the else block that follows.  This is so that the ‘EnsureSeedData’ function is called regardless to whether the environment is currently in dev mode, or prod mode.

 

Hope this helps, and happy coding! 🙂