Create a Dataverse database in Microsoft Teams

What I want to achieve in this process is to create a single Microsoft Dataverse database inside a Microsoft Teams and allow a basic Power Automate to add data to it.

image

Firstly, navigate to a Microsoft Team in the environment (here Automation), and select the + (plus) icon along the menu on the right as shown above.

image

In the list of options that appears, search for, and select Power Apps as shown above.

image

The first interesting thing, once you do that, is you typically can only select from pre-existing Power Apps that are listed in the dialog. However, there is an option create an app in Power Apps that you can select towards the bottom of the dialog as shown above.

image

You should then see the dialog display, like the one above, telling you to wait while things get set up.

image

If this process gets hung up after a minute or two, just refresh the page in your browser. You should now see something like that shown above with a list of the Microsoft Teams on the left. If you select the Microsoft Team you want to put the Dataverse database into (here Automation) you should see that nothing is built yet in the information area on the right.

image

Select the New button on the right and then App from the options that appear as shown above.

image

If you take a quick peek at the Power Platform admin center, in a new browser tab, and then Environments from the menu on the left or use the direct link:

https://admin.powerplatform.microsoft.com/environments

You’ll see that a new Power Platform environment has been created matching the name of the Microsoft Team (here Automation).

As the Microsoft documentation on Power Platform environments says:

https://docs.microsoft.com/en-us/power-platform/admin/environments-overview

A Power Platform environment is a space to store, manage, and share your organization’s business data, apps, chatbots, and flows. It also serves as a container to separate apps that might have different roles, security requirements, or target audiences.

In essence, think of an environment as a container to store things you create in the Power Platform. When you create a Power Platform App inside a Microsoft Team, it creates them in a unique container.

image

The idea is that you should be able to easily switch between environments. However, if you navigate to the Power Platform service directly at:

https://make.powerapps.com/

You are not able to see the environment just created in Microsoft Teams as shown above for some reason. It seems the only environments you can see here are those created directly in the Power Apps make portal.

image

You can drill into the new Teams environment you just created in the Power Platform admin center by selecting it from the list. Information about the environment will be displayed as shown above.

image

If you return to your app creation process inside the Microsoft Team, you’ll now need to give your app a name (here Capture)

image

Typically, you build a full app here but for now all we want to create is a single database, so select the Data icon on the left (cylinder) as shown and then select the Create new table button to the right of it.

image

You’ll then be asked to give the table a name (here Id). If you open the Advanced settings option at the bottom of the dialog, you’ll see that there are not many additional options to select from.

Select the Create button to continue.

image

You should now see the table displayed as shown above. You’ll also notice that there is already a column called Name created. This is a bit like when you create a new SharePoint list and get a single column created for you as well.

image

If you try and edit this initial column by selecting the header and then the Edit column option from the menu that appears above,

image

you’ll find there are not a lot of options available. This maybe limiting or just annoying as it is in SharePoint, but for now just leave that column in place. You’ll just need to remember to put some data in it as it is a required field.

image

You can then add any addition columns you require. Here I’ve added the columns Domain, Date and Value. These are the fields I want to populate with custom data.

image

If I return to the previous screen you should now see the Dataverse database listed as shown above.

image

Returning to the Build page in Power Apps in Microsoft Teams, and selecting the Microsoft Team (here Automation), you should now see some entries in the Items created for Automation list on the right. Here, you should also see the database just created as noted above.

image

If you select the database directly from this screen you can drill in and see the table and any entries as shown above. No data appears in the table yet as none has been added.

image

The way to get data into the database here will be via a very basic Power Automate Flow. It is a good practice to create this also inside the same Power Platform environment in which the Dataverse database was just created. Do this via the Cloud Flows option on the left as shown above.

image

To create a Flow, select Cloud Flows, then from the menu at the top on the right select the + New button. From the options that appear select Cloud Flows then type of Flow desired (here an Instant Flow).

image

The process for creating a Flow is the same as if you were creating a stand alone Flow via the Power Automate service. In this case, simply add the Dataverse Add a new row action as shown above. Configure this action to connect to the Dataverse database created earlier (Ids), then add some random text for the required default Name field (Hello), then data for Date, Domain and Value as shown above.

Save and Run the Flow.

image

If everything is correct, the Flow should run without errors as shown above.

image

If you then look at the details of the database you should see that it now has data inside it as shown above.

image

You could also create a Flow directly from the Power Automate service, but remember to switch to the new Microsoft Teams environment that was created by adding a Power Automate app to the Microsoft Team before creating the Flow.

image

The final interesting item here is to look at the capacity of the new database in the Power Platform admin center where you’ll find that, although you have a total size of 2GB, about 25% has already been consumed by the system.

For more information about the Dataverse for Teams consult the Microsoft documentation here:

About the Microsoft Dataverse for Teams environment

Using Power Automate to send SMS

I recently had the need to send an SMS programmatically. The reason I wanted to be able to do this si because Microsoft Bookings currently doesn’t send SMS reminders to attendees of a booking outside the US. That means, here in Australia, I needed to add the capability via another SMS integration option.

The most obvious choice, once again, is Power Automate. To handle the SMS component I needed to work with a SMS provider. The one who came to party was Mondotalk.

The first step was to consult the Mondotalk API to discover how to send an SMS using code. Luckily, their documentation contained a handy PowerShell script:

$messagebody1 = @{

id = $id

key = $key

username = $username

password = $password

to = “<phone>”

sender = “<phone>”

msg = “hello world”

replyto = director@ciaops.com

}

$url = <API url>

Invoke-RestMethod -Method POST -Uri $url -Body $messageBody1

In essence you create the body of the API request using an array and then you simply POST that to the API URL. Pretty straightforward and after I worked out all the variables like username, id and key, I had this working.

image

The key action is HTTP as shown above. It is important to remember that this a ‘premium’ connector and you may need a more advanced license to use this.

Initially, I thought I’d just bung in the parameters into the HTTP action as I have done before and it would be good to go. Not the case as it turned out. Everything I tried came back with a variation of the following error:

{“_meta”:{“status”:”ERROR”},”records”:{“errorCode”:401,”userMessage”:”Must login or provide credentials.”,”devMessage”:”Please provide credentials by either passing in a session token via cookie, or providing password and username via BASIC authentication.”,”more”:null,”applicationCode”:”Unauth:1″}}

In essence, the API is failing to login to my account to send the SMS. The PowerShell worked fine but Power Automate didn’t.

The solution ended up lying with adding a header field to the HTTP action:

Content type: application/x-www-form-urlencoded

and formatting the body with an ‘&’ between the fields i.e:

username=#####&password=*******&to=61######&id=#####&key=<GUID>msg=hello&replyto=director%40ciaops.com&sender=61########

as well as setting the authentication in the HTTP action to None as it was being done in the body of the request.

After all of that, I finally had success. The lesson here is that APIs don’t always want JSON!

image

I then decided to wrap a Power Virtual Agent bot around the Flow and publish it into a Team to allow users to send SMS directly from Teams. They simply use a key phrase to call the bot and tell it when number to send to. The bot passes those details to the Flow and runs what I had worked out previously

image

The only change here from the initial on demand Flow was the passing of a variable into the Flow from the bot and creating the body of the API request, also as a variable.

image

Again, it is important to get the body field in EXACTLY the right format for the API request when it is created.

So, that’s how you connect an SMS API gateway to Power Automate and Power Virtual Agents. The real trick was getting the right format of the API POST request which had to be deduced from the documentation. Hopefully, sharing that process here gives others a shortcut to getting it working in their environments because it took me a LONG time to work it out!

Thanks again to Mondotalk for being kind enough to give me a demo account and assist getting this working.

Displaying a percentage with two decimal places in Power Automate

image

In this section from my Power Automate I initialise two variables (currentscore and maxscore) with values using the Set variable action. I want a third variable, sspercentage, to be equal to:

(currentscore) / (maxscore) * 100

that is, a percentage but with only two decimal places. To do that I need to use the following formula in the Set variable 3 action:

formatNumber(mul(div(variables(‘currentscore’),variables(‘maxscore’)),100),’0.00′,’en-US’)

which you will note, if you look carefully, actually results in sspercentage being a string not a float!

(div(variables(‘currentscore’),variables(‘maxscore’))

handles the division

mul(div(variables(‘currentscore’),variables(‘maxscore’)),100)

multiples the division by 100 to get the percentage value.

The 0.00 in the formula is the actual formatting that you can adjust if you need and the en-US is the language format for the number. The Microsoft reference is for the formatnumber function is here:

https://docs.microsoft.com/en-us/azure/logic-apps/workflow-definition-language-functions-reference#formatNumber

The end result looks like:

image

If the formatnumber function is not used then the result contains a lot of numbers after the decimal place and remains of type float, which I would only need if I wanted to display the value of Pi!

Automated user tenant access control

image

If you ever used an on premises Active Directory (AD) you may be aware of the setting, shown above, that allows you to set users login times. This was typically done to prevent users logging in after hours, say from 9pm to 6am.

Unfortunately, with Azure AD there is no direct equivalent setting but we can create something similar quickly and easily using Power Automate.

image

The first step in this process is to create a new Azure AD security group that will contain the users who will be prevented from accessing the tenant. You can also create this security group in the Microsoft 365 portal but it is better to do it in Azure as you’ll need to get the ObjectID for this group as highlighted above. There is also no need to actually put any users into this group as they’ll be added dynamically by Power Automate.

image

To prevent access to the tenant you’ll need to create a Conditional Access policy as shown above. This will require you to have a license for Azure AD Premium P1 or P2 for each user. Microsoft 365 Business Premium already includes Azure AD P1, so if that is already in the environment you need nothing additional.

Give this new Conditional Access policy a name and set it to include just the Azure AD security group created previously. It is also best practice to exclude at least one administration account to prevent you from being ‘locked out’ of your tenant. Ensure that All cloud apps is selected and that Block access is configured, as shown above. Finally, turn this Conditional Access policy On.

With no members in this Azure AD Security group, no one will be restricted from accessing the tenant.

image

Next, create a List in SharePoint that contains a list of users you want to be blocked. You can achieve this by adding the Person field to the list in question. This will basically allow you to enter users who are in your tenant by doing a lookup from Azure AD.

image

Create a new Flow and use the Recurrence action to trigger it as shown above. Select an appropriate time once a day when users will be prevented from accessing the tenant, say 9pm.

image

Add the Get items action as shown above next. Configure this action to retrieve from the list of users from the SharePoint list just created.

image

Then use the Apply to each action to loop through all the users returned by this Get items as shown above.

image

Inside the Apply to each action use the Get user profile (V2) action as shown, with the users email address, which is effectively their Azure AD identity.

image

Also inside the Apply to each action add the Add user to group action as shown above. Populate this action with the ObjectID of the Azure AD security group obtained at the start and the Id from the Get user profile (V2) action.

image

Now test the Flow manually to ensure it works correctly.

image

Once the Flow completes, the users in the list in SharePoint should now appear in the Azure AD security group as shown above.

image

Now when a user on that list attempts to login, because they are part of a security group that is part of a blocking Conditional access policy, they will no longer have access to the tenant.

image

To allow access again for users simply create another scheduled Flow executing at say 6am that uses the Get group members action and then a Remove Member from group action inside an Apply to each action as shown above. In essence, this removes all users from the Azure AD security group that is part of the blocking Conditional Access policy, resulting in those users no longer being blocked from accessing the tenant.

image

Run this Flow manually and ensure it completes,

image

and you should find that Azure AD security group to be empty, as shown above.

image

The users in the list who were previously blocked,  should now be able to access the tenant as normal. Left to its own devices, users in the SharePoint list will have their access blocked from 9pm to 6am each day now.

This automation is very quick and easy to set up. It can solve the challenge of ‘forcing’ users to take a break from work after hours, rather than being ‘on’, aiding their mental health and making them more productive when they do work. It could be used to improve security but allowing account to only operate during ‘business hours’ and limiting attacks after hours, which is when many attacks happen.

This process could be extended and enhanced to provide more granular options to suit any need as well as alerting. However, hopefully, it demonstrates how easy it is to solve business challenges thanks to the Power Platform and the integration of Microsoft 365. Remember, the only extra you need is Azure AD Premium P1 to enforce Conditional Access, something already part of Microsoft 365 Business Premium.


Offboarding devices from Microsoft Defender for Business using Power Automate

Recently, I wrote an article to make offboard from Microsoft Defender for Business easier:

Offboarding devices from Microsoft Defender for Business using an API with PowerShell

Because this offboarding process utilises an API we can use that with other services such as Power Automate.

Before devices can be offboarded, a list needs to be created that can be accessed by Power Automate. Refer to this article:

Get a list of devices from Defender for Business into a SharePoint list

for details about creating an inventory of devices saved to a SharePoint Online list.

image

The summary of the Flow to do this device offboarding process is shown above.

image

Once the Flow has been triggered I grab the Azure AD application credentials from the Azure Key Vault. I’ve covered off how to create an Azure AD application here:

https://blog.ciaops.com/2019/04/17/using-interactive-powershell-to-access-the-microsoft-graph/

and using a PowerShell script I wrote here:

https://blog.ciaops.com/2020/04/18/using-the-microsoft-graph-with-multiple-tenants/

Getting the Azure AD application credentials into an Azure Key Vault can be done manually or by using this scripted process I’ve covered previously:

Uploading Graph credentials to Azure Key Vault

Once they are in the Azure Key Vault they are easy to access securely using the Flow action Get secret as shown above.

image

Next comes the Get items action as shown above. This filters the list of devices using a column called Offboard and returns items that have this as Yes (or = 1 for Power Automate).

image

A new variable is then created and the initial API offboarding URL is saved into it. This will later be appended with the actual device number that is being offboarded which is required by the API.

image

For each item that was returned from the filtered list of devices (i.e. those that been selected to offboard),

image

the offboarding API URL needed to be extended to include the unique Device ID from the returned results and the string /offboard.

image

Thus URL now needs to be ingested by the HTTP action as shown above. It is important that the body contain the following JSON:

{
   “Comment”: “Offboard machine by automation”
}

This was taken from the documentation:

Offboard machine API

The other access parameters come from Azure AD application that were extracted from Azure Key Vault earlier on in the Flow.

image

Because the return from the HTTP action can vary, we now need to have a Switch action as shown above.

image

In the top right hand corner of the Switch action, select the ellipse (three dots) and then Configure run after from the menu that appears.

image

Because the result from the HTTP action could be 400 (i.e. failure or BadRequest) we still want the Flow to proceed. If the Switch action is not used the Flow will fail like so:

image

Using the Switch action and selecting both the is successful and has failed options shown above, will allow the Flow to continue on.

image

If the HTTP action does return a BadRequest, the left hand side Case condition is met. For any other return, the right hand side Case condition will be executed.

In the case of a return status code = 400, the body of the returned JSON will be parsed and the field Result will updated in the device list for that item with the Message information taken from the JSON results.

In the case of any other return code the following will be executed:

image

Once again, there could a variety of different returned status codes from the HTTP action, however here I’ll just have a single condition to see if it is successful (status code = 201) and for anything else the results will be updated to the Result field for the device in question.

image

The last action required, after the Switch, is to reset the URL variable back to the original string in case there are other devices that have been selected to offboard. Failing to do this will result in an incorrect API URL for every device after the first match.

image

What this offboarding process looks like in practice would therefore be to select which devices to offboard from the SharePoint list, by setting the Offboard column to be Yes, as shown above.

image

Once the offboard Flow has been run, the results for those selected devices are found in the Result column and Offboard column has been reset to be No for each of these, as shown above.

image

If you set the Offboard column to Yes again for this device and re-rerun the offboarding Flow,

image

the Flow runs successfully, even though a base request resulted  during the HTTP action and the information from that is captured and stored in the Result field as shown above.

There are edge case conditions this Flows doesn’t accommodate. This is normally due to the correct information not being fully populated in the portal. This typically happens in the short period you create or add add a device to Defender for Endpoint. It is simple enough to add these checks in the Flow, but for the sake of simplicity that are not included here.

This whole process again demonstrates the flexibility and capability combining APIs with Power Automate can provide. Remember, you can set this whole process up to work across multiple tenants, it doesn’t have to be restricted to just the tenant you are on. Using Power Automate allows you to easily extend a solution to maybe include email notifications, updates into a Microsoft Team and more.

So these are some ways you can offboard devices from Microsoft Defender for Business:

Via a local script

Using Endpoint Manager and Intune

Using PowerShell

and using Power Automate as detailed here.

Celebrating anniversaries with Power Automate

A very common requirement is to remind people about anniversaries. In a business this could take the form of birthdays or commencement dates. It could, however, just as easily be any sort of event that happens on a certain date.

Previously, I’ve shown how to:

Send recurring tweets using Microsoft Flow

However, in this case, instead of simply rotating through a list of posts we want to match today’s date to a date on a list and then broadcast the message that corresponds to that date entry.

image

The starting point for this process is to create a reference list containing the dates and details you wish to share. I recommend that easiest place to do this is in a SharePoint list, as shown above. Of course, this list can contain as much detail and additional columns as you wish, but for this, I’ll keep it simple and just have two fields. It is important that you have at least one column (here Dateoption) that refers to the current year in which that item will be displayed.

image

For simplicity, I have also configured my date column in the SharePoint list to exclude time and display in standard format as shown above. There is nothing stopping you using Date & Time if you wish, it just makes the filtering a little more complex later in this process.

You’ll then want to create a Power Automate Flow that looks like this:

image

It all starts with a Recurrence action that will trigger this process once a day like so:

image

If you select the Show advanced options in this action like so:

image

You can set an exact time when this Recurrence action will be triggered (say 10am). However, since this example is a daily anniversary, we only need to trigger it at any time during the day.

image

We now need our process to determine what the current date is and we can do this using the Current time action as shown above.

image

Next, add the Convert time zone action. There are two reasons for adding this action. Firstly, the Current time action returns today’s date in UTC which may cause issues if you are not in that time zone like me. Thus, I want the current time BUT I want it as a local value (i.e. to reflect the actual time in Sydney, Australia), thus the Source and Destination time zone field settings.

The second reason for the Convert time zone action is so the time value is in the right format for a comparison test later on in the process. Thus, the Format string field should be set to yyyy-MM-dd as shown.

Now, I need to add the Get items action to actually go and look at what is in my SharePoint list.

image

In this action I enter the Site Address and List name, however I also expand the advanced options to reveal the Filter Query field as shown.

image

The Filter Query field will limit the items returned by this action to only those that match the filter. Thus, I want the returned items to only be those that match today’s date, which I have correctly formatted and stored in the Converted time action result. Thus, I want to compare the date field from my SharePoint list (here Dateoption) to the Converted time result. It is important to note that I have enclosed Converted time result in single quotes (‘) to convert the value to a string for comparison. It is very important that you do that, otherwise you’ll get errors when your Flow runs.

image

With the values that the Get Items action returns you’ll need to perform a number of steps. For this you use the Apply to each action as shown above.

image

In the case of this example, I’m simply going to post the text from the Title field in the SharePoint list that matches today’s date into a chat message as shown above. Again, this action could be anything you want, in fact, I’ll talk about how I use this with Twitter later on. For now the expectation is that if there is a match in the SharePoint list for today’s date, then the text for that entry will appear in a Teams chat message.

image

We need to do one more thing before we are finished here. As this is an anniversary calendar, we want to increment the current item for today and have it reoccur on the same date next year. To do that we use the Add to time action as shown by adding 12 months to the result date we have determined as shown above.

image

Before the new date can be added back to the SharePoint list it needs to be formatted correctly. This is achieved using the Compose action as shown using the following expression:

formatDateTime(body(‘Add_to_Time’),’yyyy-MM-dd’)

You’ll notice that that date format yyyy-MM-dd is the same as the one we set in Convert time zone action earlier.

image

All that remains is to use the Update item action to update the item in the SharePoint list with the new date entry just composed. As shown above, the same SharePoint site and list is selected, along with the item ID and Title but the Dateoption field is set to contain our new formatted date output from the previous action.

You can now save your Flow and run a manual test.

image

If I look at the chat in my Team I see the expected message that matches the item Title field in the original SharePoint list.

image

Also looking at my original SharePoint list I see that the date of today’s item has been incremented twelve months as shown.

image

One of the ways that I use this process with Twitter is to regularly post anniversary dates around ANZAC participation from World War One, which are taken from my site ANZACS in France.

The idea is that that the Flow checks this list of dates and then tweets out the text in the Title field if there is a match. Then it increments the PostDate field twelve months ready for next year. You’ll also see that I have added another custom column that records the original date of action just so I can filter and sort easily. Feel free to follow @ANZACSIF to be reminded of these dates.

As I initially mentioned, I believe there are plenty of applications for this type of process in a business. The most common ones I would suggest are for staff birthday and anniversary reminders. The great thing is that with Power Automate it is easy to modify this process to suit whatever need to have. It also makes it easy to edit the events and more if you need to because all you need to do is modify the SharePoint list that this process uses.

The possibilities are endless thanks to the Power Platform.

Adoption with fun

The majority of IT products and services are not actually used by IT people (amazing eh?). They are in fact, used by ordinary people (aka Muggels) in businesses, trying to do their job. For these people, changing the way that they work is frustrating because they need to adopt new approaches and tools. Helping with this adoption is a key to the success of modern approaches to IT I believe.

A handy technique that I have found to work well is make using new systems fun. In the distant past, when I was implementing SharePoint on premises, I used to implement the Daily Dilbert web part to post a Dilbert cartoon onto the front page of the SharePoint Intranet each day. The idea was to help drive adoption by getting people to visit the company Intranet to read the Dilbert comic and then, hopefully, dive into the other content that was there.

Today, the technology has changed but the adoption challenge hasn’t. I thought that I’d therefore share with you a way to get a Dilbert comic into your Teams channel daily using Power Automate.

This is all made possible via APIs and a suitable one I found is:

https://dilbert-api.glitch.me/json

which will produce an output that looks like this:

{"title":"Simulation TestingElbonia University Partial Win","image":"https://assets.amuniversal.com/4f2025a02e0d013a8769005056a9545d.png"}

In here you’ll see an image link to the Dilbert Cartoon.

Step one is to create a new Flow that is triggered at a recurring time.

image

Next, you want to add the HTTP action. In here, use the GET method and the URI set to the above API link as shown above.

The HTTP action is actually a ‘premium’ connector and may not be available to you by default. Thus, you may need an upgraded Power Platform license to have this available. Remember however, you’ll only need that license for the user creating and running that Flow.

image

You’ll then need to the Parse JSON action as shown above. The content here will be the Body from the HTTP action above and simply copy and paste the output of the API above into the option Generate from sample.

image

Now add Post message in a chat or channel action.

image

Enter option to post into the Team and Channel of your choice as shown above.

image

For the Message field select the </> option from the menu bar across the top, as shown. This will allow you to use raw HTML code here.

Type the following:

<img src = ”

then select the option to insert dynamic content like so:

image

(the lightning bolt icon)

image

In this list that appears you should be able to select image as shown above.

image

add the following text after the dynamic field

” width=”738″ height=”229″>

so the completed Message field looks like:

image

It is important that the HTML formatting is correct, otherwise the image will not display.

image

If you now test your Flow you should see the cartoon appear in your Teams channel as shown above. If you have scheduled your Flow daily then you should see a new comic every day. Remember, there is only one cartoon every 24 hours! Rerunning the Flow before then will simply display the same strip.

When the daily comic is more than three frames then it is cut off by default like so:

image

However, clicking on the comic will enlarge it for full viewing. This limitation is due to the height and wide parameters the HTML code used inside the Flow. Most strips are only three frames, that is why I used those height and width defaults for most readability most of the time, but you can vary those parameter if you wish.

So, the idea is to make visiting a Team a more fun place to visit regularly, hopefully with people engaging about the content to help drive adoption.

This Flow/API method can be utilised with just about anything that supports an API. Another I have found (although somewhat more risqué) is a Chuck Norris API here:

https://api.chucknorris.io/

which can be moulded to give a similar result (be it text only).

The only limitation of all of this is the need for the premium Flow HTTP action, but as I said, it is well worth the investment and is only really necessary for the user creating the Flow. Having a premium license for Flow opens up so many more capabilities, so it is highly recommended if you want to get serious about automation inside your environment.

Happily, Daily Dilbert is back baby! And now in Microsoft Teams.

Power Platform product release

pexels-clem-onojeghuo-175711 (1)

I am please to announce the inaugural product release from the CIAOPS Patron Power Platform Community:

Power Automate Drink Ordering System

This is a PDF document that takes your step by step through creating an automated process for bulk ordering of drinks. This solution was developed to solve the challenge or ordering many drinks at events, however it could be used for much more mundane things such as preparing complimentary drinks when guests attend a business.

This project can also be seen as a great way to start learning the Microsoft Power Platform by creating a real world process using Power Automate (Microsoft Flow). The information provided run to over 130 pages and includes screen shots and easy to follow instructions.

To celebrate the release of this inaugural product, it is being offered at a special 75% discount which you can take advantage of here:

https://directorcia.gumroad.com/l/lDekX/foundation

This special offer is available for the first 25 purchases! So if you are interested in getting your hands dirty with the Microsoft Power Platform, here’s a great opportunity to get started.

Look out for more projects coming soon from the CIAOPS Patron Power Platform Community.