Support Power Platform - Apps - Automate - SharePoint - Copilot

[Dev] Création d’une liste SharePoint depuis Node.js

Stack : SPFx + PnPjs v4 + certificat Azure AD

Phase 1 — Préparation du projet

Étape 1 — Créer le projet SPFx

md fil-rouge-webpart && cd fil-rouge-webpart
yo @microsoft/sharepoint
# Répondre : WebPart › React › TypeScript › nom : VueProd

Étape 2 — Installer les dépendances

npm install @pnp/sp @pnp/nodejs dotenv node-forge
npm install --save-dev ts-node @types/node @types/node-forge

Étape 3 — Créer tsconfig.scripts.json à la racine

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": false,
    "outDir": "./dist-scripts"
  },
  "include": ["scripts/**/*.ts"]
}

Étape 4 — Ajouter les scripts dans package.json

Dans la section "scripts" du fichier package.json existant, ajouter :

"export-key":  "ts-node --project tsconfig.scripts.json scripts/exportKey.ts",
"create-list": "ts-node --project tsconfig.scripts.json scripts/createList.ts"

Phase 2 — Certificat et Azure AD

Étape 5 — Générer le certificat (PowerShell en administrateur)

$cert = New-SelfSignedCertificate `
  -Subject "CN=AppOffice365" `
  -CertStoreLocation "Cert:\CurrentUser\My" `
  -KeyExportPolicy Exportable `
  -KeySpec Signature `
  -KeyLength 2048 `
  -HashAlgorithm SHA256 `
  -NotAfter (Get-Date).AddYears(2)


# Exporter le certificat public (.cer) pour Azure AD
Export-Certificate -Cert $cert `
  -FilePath ".\scripts\appcert.cer"


# Exporter la clé privée (.pfx) pour Node.js
$pwd = ConvertTo-SecureString -String "CertPassword123!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert `
  -FilePath ".\scripts\appcert.pfx" `
  -Password $pwd

Étape 6 — Uploader le certificat dans Azure AD

  1. Aller sur portal.azure.com
  2. Azure Active DirectoryInscriptions d’applicationsAppOffice365
  3. Certificats et secrets › onglet CertificatsTélécharger le certificat
  4. Choisir le fichier scripts/appcert.cer
  5. Noter le thumbprint affiché après l’upload

Étape 7 — Ajouter la permission SharePoint Application

  1. Autorisations APIAjouter une autorisation
  2. Choisir SharePoint (pas Microsoft Graph)
  3. Cliquer Autorisations d’application (pas Déléguées)
  4. Cocher Sites.FullControl.All
  5. Cliquer Accorder le consentement administrateur pour SARL IN SET

Résultat attendu :

APIAutorisationTypeStatut
SharePointSites.FullControl.AllApplicationAccordé

Étape 8 — Créer scripts/exportKey.ts et convertir la clé

import * as forge from "node-forge";
import * as fs from "fs";


const pfx = forge.pkcs12.pkcs12FromAsn1(
  forge.asn1.fromDer(
    forge.util.createBuffer(
      fs.readFileSync("./scripts/appcert.pfx").toString("binary")
    )
  ),
  "CertPassword123!"
);


const bags = pfx.getBags({
  bagType: forge.pki.oids.pkcs8ShroudedKeyBag
});


const pem = forge.pki.privateKeyToPem(
  bags[forge.pki.oids.pkcs8ShroudedKeyBag]![0].key!
);


fs.writeFileSync("./scripts/private.key", pem, "utf-8");
console.log("Clé PEM exportée avec succès.");

Lancer la conversion :

npm run export-key

Étape 9 — Créer le fichier .env

TENANT_ID=xxx
CLIENT_ID=xxx
THUMBPRINT=xxx
KEY_PATH=./scripts/private.key
SITE_URL=https://<tenant>.sharepoint.com/sites/<site>
SP_ROOT_URL=https://<<tenant>.sharepoint.com
Ne jamais commiter ce fichier dans Git — l’ajouter dans .gitignore.

Phase 3 — Script de création et exécution

Étape 10 — Créer scripts/createList.ts

import "dotenv/config";
import { spfi } from "@pnp/sp";
import { SPDefault } from "@pnp/nodejs";
import * as fs from "fs";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/fields";


const TENANT_ID   = process.env.TENANT_ID!;
const CLIENT_ID   = process.env.CLIENT_ID!;
const THUMBPRINT  = process.env.THUMBPRINT!;
const KEY_PATH    = process.env.KEY_PATH!;
const SITE_URL    = process.env.SITE_URL!;
const SP_ROOT_URL = process.env.SP_ROOT_URL!;


async function createSessionsList(): Promise<void> {


  const privateKey = fs.readFileSync(KEY_PATH, "utf-8").trim();


  const sp = spfi(SITE_URL).using(
    SPDefault({
      baseUrl: SITE_URL,
      msal: {
        config: {
          auth: {
            clientId:  CLIENT_ID,
            authority: `https://login.microsoftonline.com/${TENANT_ID}`,
            clientCertificate: {
              thumbprint: THUMBPRINT,
              privateKey: privateKey
            }
          }
        },
        scopes: [`${SP_ROOT_URL}/.default`]
      }
    })
  );


  console.log("Création de la liste Sessions...");
  await sp.web.lists.add("Sessions", "Liste des sessions de formation", 100);


  const list = sp.web.lists.getByTitle("Sessions");
  console.log("Ajout des colonnes...");


  // Colonnes texte
  for (const col of [
    "CodeFormation", "HeureDebut", "NumeroSession",
    "TypeFormation", "NomClient", "Site",
    "CoordinationNom", "CoordinationEmail",
    "Modalite", "LienTeams", "LienSupport",
    "LienParticipants", "NomAgence", "EmailAgence",
    "LienEmargement", "LienEvaluation", "FichierIcs"
  ]) {
    await list.fields.addText(col);
    console.log(`  + ${col}`);
  }


  // Colonnes date
  await list.fields.addDateTime("DateSession");
  await list.fields.addDateTime("DateFin");
  console.log("  + DateSession, DateFin");


  // Colonnes nombre
  await list.fields.addNumber("Duree");
  await list.fields.addNumber("NbConfirmes");
  await list.fields.addNumber("NbOption");
  await list.fields.addNumber("NbDistanciel");
  console.log("  + Duree, NbConfirmes, NbOption, NbDistanciel");


  // Colonne texte long
  await list.fields.addMultilineText("EnjeuxContexte");
  console.log("  + EnjeuxContexte");


  // Colonne choix
  await list.fields.addChoice("Statut", {
    Choices: ["Confirmé", "Option"]
  });
  console.log("  + Statut");


  console.log("Liste Sessions créée avec succès.");
}


createSessionsList()
  .then(() => process.exit(0))
  .catch((err) => {
    console.error("Erreur :", err.message ?? err);
    process.exit(1);
  });

Étape 11 — Lancer la création de la liste

cd C:\Users\gonza\fil-rouge-webpart
npm run create-list

Résultat attendu :

Création de la liste Sessions...
Ajout des colonnes...
  + CodeFormation
  + HeureDebut
  ...
  + Statut
Liste Sessions créée avec succès.

Structure finale des fichiers

fil-rouge-webpart/
├── scripts/
│   ├── appcert.cer        ← certificat public (uploadé dans Azure AD)
│   ├── appcert.pfx        ← certificat + clé privée (ne pas commiter)
│   ├── private.key        ← clé PEM exportée (ne pas commiter)
│   ├── exportKey.ts       ← script de conversion PFX → PEM
│   └── createList.ts      ← script de création de la liste
├── src/
├── .env                   ← variables sensibles (ne pas commiter)
├── tsconfig.scripts.json  ← config TypeScript pour les scripts
└── package.json

Points de sécurité

  • Ajouter dans .gitignore : .env, scripts/appcert.pfx, scripts/private.key
  • Le certificat a une durée de validité de 2 ans — penser à le renouveler avant expiration
  • La permission Sites.FullControl.All donne un accès total à tous les sites — envisager Sites.Selected en production pour limiter le périmètre