Power Automate: how to verify if a property belongs to an object (apply to SharePoint Date too)

We can use this kind of formula (notice the questionmark):
if(empty(variables('params')?[variables('fieldName')]), 'fieldName is not part of the object params', variables('params')?[variables('fieldName')])

Then if it either returns “fieldName is not part of the object params” or the value.

We can use it to check if a date field is empty in a SharePoint List, because when getting the data from the SP List, when the date field is empty, it’s not in the result returned – the below code will return true if the date field is empty:
empty(item()?['dateFieldNameId'])

For a Currency/Number field you need to use string() as well:
empty(string(item()?['floatFieldNameId']))

Power Automate returns an error about “InvokerConnectionOverrideFailed” and “header.X-MS-APIM-Tokens”

While calling a “Run a Child Flow” from a Power Automate Flow, you could get an error about “InvokerConnectionOverrideFailed” and “header.X-MS-APIM-Tokens”.

After investigating, to resolve this issue you need to open the “details” view of your child flow, and click on the “Edit” button from the “Run only users” card:

Then in the Connections Used section, make sure to select “Use this connection (your_username@domain.com)” instead of the default “Provided by run-only user”. It will permit to run this child flow with your rights.

Supprimer la commande Ctrl+Alt+Supp pour ouvrir Windows 11

Il suffit de modifier le registre de Windows (regedit), en passant à 1 la variable DisableCad dans ces deux emplacements :

  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon

Search and restore an item from a SharePoint Online Recycle Bin

It might be difficult to search for an item in a SharePoint recycle bin. I was using the end point _api/site/RecycleBin as a Site Collection Administrator, but in some cases it returns an error “The attempted operation is prohibited because it exceeds the list view threshold.”.

The solution is to use another end point _api/site/getrecyclebinitems (see the documentation for the various parameters):

fetch("https://tenant.sharepoint.com/sites/YourSite/_api/site/getrecyclebinitems?rowLimit=%2770000%27&itemState=0&orderby=3&isAscending=false", {
  "headers": {
    "accept": "application/json",
    "content-type": "application/json",
    "x-requestdigest": document.querySelector("#__REQUESTDIGEST").value
  },
  "method": "GET",
})
.then(res => res.json())
.then(json => {
  console.log(json.value);
  // it will show an array with an object that contains several interesting properties:
  // AuthorEmail, AuthorName, DeletedByEmail, DeletedDate, DirName, Id, LeafName, Title, …
})

You can then filter the result to get what you need, for example if you’re looking for an item from the list “Projects” that has the ID 1981:

json.value.filter(item => item.DirName.includes("Lists/Projects") && item.LeafName === "1981_.000");

Then, to restore an item, we need the Id from the previous result. The famous endpoint to restore an item is _api/site/RecycleBin('Id')/restore, however it could also return the error “The attempted operation is prohibited because it exceeds the list view threshold”. In that case, we can use this other endpoint _api/site/RecycleBin/RestoreByIds:

fetch("https://tenant.sharepoint.com/sites/YourSite/_api/site/RecycleBin/RestoreByIds", {
  "headers": {
    "accept": "application/json",
    "content-type": "application/json",
    "x-requestdigest": document.querySelector("#__REQUESTDIGEST").value
  },
  "method": "POST",
  "body":JSON.stringify({
    "ids": [
      "4f855ee7-472b-414a-a482-4317a114c1a2" // Id to restore
    ],
    "bRenameExistingItems": true
  })
})

Calculating HMAC SHA-1 in the Browser

If you’re looking for the equivalent of hash_hmac('sha1', 'string', 'secret'); in JavaScript, then here you go:

async function hmac_sha1 (str, secret) {
  // see https://stackoverflow.com/a/47332317/1134119
  let enc = new TextEncoder("utf-8");
  let key = await window.crypto.subtle.importKey(
    "raw", // raw format of the key - should be Uint8Array
    enc.encode(secret),
    { // algorithm details
      name: "HMAC",
      hash: {name: "SHA-1"}
    },
    false, // export = false
    ["sign", "verify"] // what this key can do
  );
  let signature = await window.crypto.subtle.sign(
    "HMAC",
    key,
    enc.encode(str)
  );
  let b = new Uint8Array(signature);
  return Array.prototype.map.call(b, x => x.toString(16).padStart(2, '0')).join("");
}

Connect to SharePoint Online using an app clientId and clientSecret

Get `clientId` and `clientSecret`

(source)

You’ll need credentials:

  • `clientId` – required string, client id obtained when registering the addin
  • `clientSecret` – required string, client secret obtained when registering the addin
  • `realm` – your SharePoint Online tenant id. The easiest way to find tenant is to open SharePoint Online site collection, click Site SettingsSite App Permissions. Under this page you wll see at least one app “Microsoft.SharePoint”. The tenant id (realm) is highlighted in the image below:

Example of the expected result:

{
  clientId: '28bq7e56-8c3a-487d-hbfb-ef1a74539cbe',
  clientSecret: 's6LZ4VvoeKOS+MyAhklcavsyJBF4XhWo06OgY6czYJ0=',
  realm: '85e5f09b-4c17-4d80-afea-260bb171c456'
}

To get the credentials, you need to register a new addin inside SharePoint Online, by fellowing these steps:

  1. Open SharePoint Online app registration page, e.g. https://contoso.sharepoint.com/sites/dev/_layouts/15/appregnew.aspx
  2. Click on “Generate” for Client id and Client Secret, fill in Title, App Domain, Redirect URI (you can type in any values you want)
  3. Click on “Create” and save generated Client Id and Client Secret
  4. [IF YOU HAVE TENANT RIGHTS] Now you need to apply permissions to the newly registered app. If you want to register the app once and use it for any site collection, it’s better to apply tenant scope permissions, so you can use the credentials everywhere inside your SharePoint tenant. To apply tenant scoped permissions, open AppInv.aspx page under SharePoint adminstration web site, e.g. https://[YOUR_ORGANIZATION]-admin.sharepoint.com/_layouts/15/appinv.aspx, copy paste Client Id from step n°3 into App Id field and click “Lookup”.
  5. [IF YOU HAVE TENANT RIGHTS] You will see your registered app, paste in the following XML into the “Permission Request XML” field and click “Create”:
        <AppPermissionRequests AllowAppOnlyPolicy="true">
          <AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
        </AppPermissionRequests>
    

  6. [IF YOU ARE NOT A TENANT] If you only want to give permissions on 1 site collection, you can register the app on a regular site collection by using url https://contoso.sharepoint.com/sites/dev/_layouts/15/appinv.aspx. In this case you are not able to use tenant scoped permissions and can only apply site collection permissions:
        <AppPermissionRequests AllowAppOnlyPolicy="true">
          <AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
        </AppPermissionRequests>
    
  7. You will see addin “Trust” confirmation, click on “Trust It”:

    if trust-it button is not enabled and you get a red label saying tenant admin needs to trust the app, go back and try again in a few minutes.
  8. Now you can use client id and client secret to send authenticated http requests.

To know more about the XML permissions, you can check the Microsoft documentation.

Get Access Token

(you can find a C# code as an example)

You need to do a POST request to https://accounts.accesscontrol.windows.net/[YOUR_TENANT_REALM]/tokens/OAuth/2 with a “Content-Type” header that has the value “application/x-www-form-urlencoded”, and the body parameters that must be:

  • "grant_type":"client_credentials"
  • "client_id":"[YOUR_CLIENT_ID]@[YOUR_TENANT_REALM]"
  • "client_secret":"[YOUR_CLIENT_SECRET]"
  • "resource":"00000003-0000-0ff1-ce00-000000000000/dell.sharepoint.com@[YOUR_TENANT_REALM]"

See below an example in PHP:

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "https://accounts.accesscontrol.windows.net/[YOUR_TENANT_REALM]/tokens/OAuth/2");
curl_setopt($curl, CURLOPT_HTTPHEADER, [ "Content-Type: application/x-www-form-urlencoded" ]);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query([
  "grant_type" => "client_credentials",
  "client_id" => "[YOUR_CLIENT_ID]@[YOUR_TENANT_REALM]",
  "client_secret" => "[YOUR_CLIENT_SECRET]",
  "resource" => "00000003-0000-0ff1-ce00-000000000000/dell.sharepoint.com@[YOUR_TENANT_REALM]"
]));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($curl));
curl_close($curl);

echo $response->access_token;

The response should contain an access token. Example:

{
  "token_type":"Bearer",
  "expires_in":"86399",
  "not_before":"1679393911",
  "expires_on":"1679480611",
  "resource":"00000003-0000-0ff1-ce00-000000000000/dell.sharepoint.com@[YOUR_TENANT_REALM]",
  "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSU[...]SxXA5Lqbk1OcOVdwQ"
}

Finally, you can do your REST API request to SharePoint Online with passing the header “Authorization” that has the value “Bearer [YOUR_ACCESS_TOKEN]”.

Mount a SMB Network Drive with Pivotal Cloud Foundry (pcf) and access it using NodeJS

Connect to the Apps Manager and navigate to the space where you want to mount the drive. Look at the members, and make sure your user has the correct roles:

Then in a console, connect and log into the pcf space (something like cf login and cf -s APPNAME).

Next, try the command cf create-service smb Existing Files_Share -c '{\"share\":\"//server.name.domain.com/Files_Share\"}' (the backslashes are important if you’re on Windows).

Now you can bind the service with cf bind-service APPNAME Files_Share -c '{\"username\":\"username_without_domain\",\"password\":\"network_password\",\"mount\":\"/mnt/Files_Share\"}'

For more info, see https://docs.cloudfoundry.org/devguide/services/using-vol-services.html#smb

spfx error: No development certificate found. Generate a new certificate manually, or set the `canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`

When using the command gulp serve, you could receive the below error:

No development certificate found. Generate a new certificate manually, or set the `canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`

To resolve, you can type gulp trust-dev-cert.

Deploy a PCF NodeJS app as a scheduled task

I have a NodeJS app that runs as a process and that executes a task every 15 minutes using node-schedule.

We first need a manifest.yml file that contains:

---
applications:
- name: APP-NAME
  buildpack: nodejs_buildpack
  no-route: true
  health-check-type: process
  env:
    OPTIMIZE_MEMORY: true

The no-route parameter is true so that we don’t get a route assigned, and the health-check-type is set to process so that the orchestrator monitors process availability and doesn’t try to ping a non-existent web endpoint. And OPTIMIZE_MEMORY in “env” section is based on the Pivotal recommendations.

If you need to use a local package in your app, you’ll have to pack it up first. To do it, go to your local module folder, and type npm pack. It will create a .tgz file that you’ll have to store in a local_modules folder for your app. Next, use npm install .\local_modules\package-1.2.3.tgz.

You can now deploy your app with pcf push APP-NAME and you can read the logs with cf logs APP-NAME --recent.

Power Automate: execute a SQL Query via On-Promise Gateway

In Power Automate, when you want to connect to a SQL Server and if you have a On-Promise Gateway, then you cannot use the command “Execute a SQL Query” because it will say it’s not currently supported.

There is a workaround with “Transform data using Power Query” (ATTENTION: you cannot load it from a flow from a Solution… you’ll have to go to your Flows and edit the flow from there):

Let’s say we have 3 tables: ITEM_CATALOG, CATALOG and CURRENCY. We want to join them and filter them based on a variable found previously in our flow.

First, we can define our where. Here I have several values that I want to test using a IN:

I create a string with my different values separated by a coma.

Next, we can open the Power Query editor:

In the interface, we choose the 3 tables we need to merge and we add a parameter called “where”:

We rename it to “where” and leave the default settings:

Then we use the “Advance Editor”:

And we wrote the below:

let
  where = Text.Split( "@{variables('where')}" , ",")
in
  where

It means we want to split the variable “where” coming from the flow, based on the coma separator:

We can now merge the tables and add a filter:

And when the step to filter is here, we select “in” and our query:

Last step is to “Enable Load” to make sure this is what the operation will return to our flow:

You can run it to test and see if it works.

Then, to get the output from it, we’ll use a “Parse JSON”… The schema is probably something like:

{
    "type": "object",
    "properties": {
        "resultType": {
            "type": "string"
        },
        "value": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "COLUMN_A": {
                        "type": "string"
                    },
                    "COLUMN_B": {
                        "type": "integer"
                    },
                    "COLUMN_C": {
                        "type": "string"
                    }
                },
                "required": [
                    "COLUMN_A",
                    "COLUMN_B",
                    "COLUMN_C"
                ]
            }
        }
    }
}

You may need to make several tries in order to find the correct schema. You can also use the “Generate from sample” by pasting the data from the previous step:

We use “value” in the loop:

And then we can access our columns: