Add configuration files and update imports for sk-az-tools integration

This commit is contained in:
2026-02-05 06:09:00 +01:00
parent 46c3c97e63
commit 88a81842f9
9 changed files with 154 additions and 85 deletions

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"files.exclude": {
"node_modules": true
}
}

View File

@@ -0,0 +1,16 @@
{
"folders": [
{
"path": "."
},
{
"path": "../sk-az-tools"
},
{
"path": "../docs-harvester-node"
}
],
"settings": {
}
}

View File

@@ -1,5 +1,5 @@
#! /usr/bin/env node #! /usr/bin/env node
import { loginInteractive } from "../src/azure.js"; import { loginInteractive } from "sk-az-tools/azure";
import { Client } from "@microsoft/microsoft-graph-client"; import { Client } from "@microsoft/microsoft-graph-client";
const scopes = [ const scopes = [

View File

@@ -2,7 +2,7 @@
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { parseArgs } from "util"; import { parseArgs } from "util";
import { getCredential } from "../src/azure.js"; import { getCredential } from "sk-az-tools/azure";
let config = {}; let config = {};

View File

@@ -9,7 +9,7 @@ const args = parseArgs({
"app-name": { type: "string", short: "a" }, "app-name": { type: "string", short: "a" },
help: { type: "boolean", short: "h" }, help: { type: "boolean", short: "h" },
"generate-client-secret": { type: "boolean", short: "s" }, "generate-client-secret": { type: "boolean", short: "s" },
"config": { type: "string", short: "c", default: "config.js" }, "config": { type: "string", short: "c", default: "config.json" },
"write-config": { type: "string", short: "w" }, "write-config": { type: "string", short: "w" },
}, },
}); });

45
package-lock.json generated
View File

@@ -10,7 +10,28 @@
"dependencies": { "dependencies": {
"@azure/identity": "^4.13.0", "@azure/identity": "^4.13.0",
"@azure/msal-node": "^5.0.2", "@azure/msal-node": "^5.0.2",
"@microsoft/microsoft-graph-client": "^3.0.7" "@microsoft/microsoft-graph-client": "^3.0.7",
"docs-harvester": "file:../docs-harvester-node",
"sk-az-tools": "file:../sk-az-tools"
},
"engines": {
"node": ">=24.0.0"
}
},
"../docs-harvester-node": {
"name": "docs-harvester",
"version": "0.1.0",
"bin": {
"docs-harvester": "src/cli.js"
}
},
"../sk-az-tools": {
"version": "0.1.0",
"dependencies": {
"@azure/identity": "^4.13.0",
"@azure/msal-node": "^5.0.3",
"@microsoft/microsoft-graph-client": "^3.0.7",
"azure-devops-node-api": "^15.1.2"
}, },
"engines": { "engines": {
"node": ">=24.0.0" "node": ">=24.0.0"
@@ -175,12 +196,12 @@
} }
}, },
"node_modules/@azure/msal-node": { "node_modules/@azure/msal-node": {
"version": "5.0.2", "version": "5.0.3",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.0.2.tgz", "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.0.3.tgz",
"integrity": "sha512-3tHeJghckgpTX98TowJoXOjKGuds0L+FKfeHJtoZFl2xvwE6RF65shZJzMQ5EQZWXzh3sE1i9gE+m3aRMachjA==", "integrity": "sha512-DTiu9SEblUVbgiuXWtCXFS2OoIVZPNbPFfK7AVhsWCD4bCHqvHcnDz9uCxk5wnUYJX9Ik0KxjGDFLwH9/lrE+w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@azure/msal-common": "16.0.2", "@azure/msal-common": "16.0.3",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"uuid": "^8.3.0" "uuid": "^8.3.0"
}, },
@@ -189,9 +210,9 @@
} }
}, },
"node_modules/@azure/msal-node/node_modules/@azure/msal-common": { "node_modules/@azure/msal-node/node_modules/@azure/msal-common": {
"version": "16.0.2", "version": "16.0.3",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.0.2.tgz", "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.0.3.tgz",
"integrity": "sha512-ZJ/UR7lyqIntURrIJCyvScwJFanM9QhJYcJCheB21jZofGKpP9QxWgvADANo7UkresHKzV+6YwoeZYP7P7HvUg==", "integrity": "sha512-3aedNnM0CHVuVZ+BqembdZWgovqe96BJ4YxGoIK0+qhoBZQsAhfwXdhjen72K94pkSQHtzlJ7fAq6w7knFZsng==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
@@ -334,6 +355,10 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/docs-harvester": {
"resolved": "../docs-harvester-node",
"link": true
},
"node_modules/ecdsa-sig-formatter": { "node_modules/ecdsa-sig-formatter": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -570,6 +595,10 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/sk-az-tools": {
"resolved": "../sk-az-tools",
"link": true
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",

View File

@@ -8,6 +8,8 @@
"dependencies": { "dependencies": {
"@azure/identity": "^4.13.0", "@azure/identity": "^4.13.0",
"@azure/msal-node": "^5.0.2", "@azure/msal-node": "^5.0.2",
"@microsoft/microsoft-graph-client": "^3.0.7" "@microsoft/microsoft-graph-client": "^3.0.7",
"docs-harvester": "file:../docs-harvester-node",
"sk-az-tools": "file:../sk-az-tools"
} }
} }

27
src/prototypes/devops.mjs Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env node
// The current Node.js Azure DevOps client uses old deprecated APIs.
import { config } from "../../public-config.js";
import { getDevOpsClients } from "sk-az-tools/devops";
async function main() {
const { coreClient, gitClient } = await getDevOpsClients(
'https://dev.azure.com/skoszewski',
config.tenantId,
config.clientId,
);
const orgUrl = 'https://dev.azure.com/skoszewski';
const projects = await coreClient.getProjects();
console.log(`Projects in organization '${orgUrl}':`);
projects.forEach((project) => {
console.log(`- ${project.name} (ID: ${project.id})`);
});
}
await main().catch((e) => {
console.error("Error in main:", e);
console.error(e.stack);
process.exit(1);
});

View File

@@ -1,82 +1,72 @@
#!/usr/bin/env node #!/usr/bin/env node
import { loginInteractive } from "sk-az-tools/azure"; import { config } from "../../public-config.js";
import { config } from "../public-config.js"; import { getGraphClient } from "sk-az-tools/graph";
import { createHash } from "crypto";
import { Client } from "@microsoft/microsoft-graph-client";
// const scopes = ["https://management.azure.com/.default"]; async function main() {
const scopes = ["https://graph.microsoft.com/.default"]; const { client } = await getGraphClient({
let token;
try {
token = await loginInteractive({
tenantId: config.tenantId, tenantId: config.tenantId,
clientId: config.clientId, clientId: config.clientId,
scopes,
}); });
} catch (e) {
console.error("Login failed:", e);
console.error(e.stack);
process.exit(1);
}
console.log("Access token acquired."); // Let's find the application by its display name
let result;
// Print SHA-256 hash of the access token for verification purposes // First get info about me.
const hash = createHash("sha256").update(token.accessToken).digest("hex"); result = await client.api("/me").get();
console.log("SHA-256 hash of access token:", hash); console.log("Logged in as:");
console.log("Token expires on:", token.expiresOn); console.log(` Display Name: ${result.displayName}`);
console.log(` User Principal Name: ${result.userPrincipalName}`);
console.log(` ID: ${result.id}`);
console.log("");
const client = Client.init({ // Now let's find the application by its display name
authProvider: (done) => {
done(null, token.accessToken);
},
});
let result; result = await client
result = await client
.api("/applications") .api("/applications")
.filter("displayName eq 'Azure Node Playground Public'") .filter("displayName eq 'Azure Node Playground Public'")
.get(); .get();
const apps = result.value ?? []; const apps = result.value ?? [];
console.log( console.log(
`Registered applications with the name 'Azure Node Playground' (${apps.length}):`, `Registered applications with the name 'Azure Node Playground' (${apps.length}):`,
); );
if (apps.length !== 1) { if (apps.length !== 1) {
console.error( console.error(
"Expected exactly one application with the name 'Azure Node Playground'. Please ensure it is registered in your Azure AD tenant.", "Expected exactly one application with the name 'Azure Node Playground'. Please ensure it is registered in your Azure AD tenant.",
); );
process.exit(1); process.exit(1);
} }
console.log(`Application ID: ${apps[0].appId}`); console.log(`Application ID: ${apps[0].appId}`);
// Let's find the service principals for this application // Let's find the service principals for this application
result = await client result = await client
.api("/servicePrincipals") .api("/servicePrincipals")
.filter(`appId eq '${apps[0].appId}'`) .filter(`appId eq '${apps[0].appId}'`)
.get(); .get();
const sps = result.value ?? []; const sps = result.value ?? [];
console.log( console.log(`Service principals for the application (${sps.length}):`);
`Service principals for the application (${sps.length}):`,
);
if (sps.length === 0) { if (sps.length === 0) {
console.error( console.error(
"No service principals found for the application. Please ensure the application is properly configured in your Azure AD tenant.", "No service principals found for the application. Please ensure the application is properly configured in your Azure AD tenant.",
); );
process.exit(1); process.exit(1);
} }
sps.forEach((sp, index) => { sps.forEach((sp, index) => {
console.log(`Service Principal ${index + 1}:`); console.log(`Service Principal ${index + 1}:`);
console.log(` ID: ${sp.id}`); console.log(` ID: ${sp.id}`);
console.log(` App ID: ${sp.appId}`); console.log(` App ID: ${sp.appId}`);
console.log(` Display Name: ${sp.displayName}`); console.log(` Display Name: ${sp.displayName}`);
});
}
await main().catch((e) => {
console.error("Error in main:", e);
console.error(e.stack);
process.exit(1);
}); });