Skip to content

Commit

Permalink
Better handling of logs
Browse files Browse the repository at this point in the history
  • Loading branch information
DrizzlyOwl committed Mar 11, 2024
1 parent 101b64a commit 1897723
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 56 deletions.
5 changes: 4 additions & 1 deletion azure-function/local.settings.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
"AZURE_SUBSCRIPTION": null,
"AZURE_TENANT_ID": null,
"AZURE_CLIENT_ID": null,
"AZURE_USE_UAMI": false,
"AZURE_CLIENT_SECRET": null,
"SLACK_WEBHOOK_URL": null
"SLACK_WEBHOOK_URL": null,
"APPLICATIONINSIGHTS_CONNECTION_STRING": "",
"NODE_ENV": "development"
}
}
161 changes: 106 additions & 55 deletions azure-function/src/functions/scheduledCronTrigger.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,98 @@
require("dotenv/config");
const { IncomingWebhook } = require('@slack/webhook');
const { DefaultAzureCredential, ManagedIdentityCredential, ChainedTokenCredential } = require("@azure/identity");
const { app } = require('@azure/functions');
const { ManagedIdentityCredential, AzureCliCredential, ClientSecretCredential, AzureDeveloperCliCredential, DefaultAzureCredential } = require("@azure/identity");
const { app, HttpRequest } = require('@azure/functions');
const { KeyVaultManagementClient } = require("@azure/arm-keyvault")
const { SecretClient } = require("@azure/keyvault-secrets");
const { setLogLevel } = require("@azure/logger");
const { SubscriptionClient } = require("@azure/arm-subscriptions");

const environment = process.env["APPSETTING_NODE_ENV"] || process.env["NODE_ENV"];
console.log("Node Environment set to", environment)

// Set logging level to "warning" for Prod, or "info" for Dev
const logLevel = environment && environment === "production" ? "error" : "warning"
const logLevel = environment && environment === "production" ? "error" : "info"
console.info("Setting Log Level to", logLevel)
setLogLevel(logLevel)

// These are environment variables that should be passed to the Azure function
// const azure_tenantId = process.env["AZURE_TENANT_ID"];
// const azure_clientId = process.env["AZURE_CLIENT_ID"];
// const azure_secret = process.env["AZURE_CLIENT_SECRET"];
const azure_uami_clientId = process.env["AZURE_UAMI_CLIENT_ID"];
const azure_tenantId = process.env["AZURE_TENANT_ID"];
const azure_clientId = process.env["AZURE_CLIENT_ID"];
const azure_secret = process.env["AZURE_CLIENT_SECRET"];
const azure_uami = process.env["AZURE_USE_UAMI"];
const azure_subscription = process.env["AZURE_SUBSCRIPTION"];
const slack_webhook_url = process.env["SLACK_WEBHOOK_URL"];

let today = new Date()

const credentials = getCredentials()
let credentials

/**
* Initialise the authentication handler and begin
* @returns void
*/
function init(request, context) {
/**
* Make Context available to all
*/
process.context = context

/**
* Determine if the authentication was successful
*/
credentials = getCredentials(context)

/**
* If credentials is not null, then we can try to grab a Bearer token
*/
if (credentials) {
context.log("Testing credentials by acquiring a token...")

credentials.getToken().then((token) => {
context.log("Successfully acquired token")
start()
}, (CredentialUnavailableError) => {
context.error("Failed to acquire a token", CredentialUnavailableError)
})
}
}

/**
* Load an appropriate set of credentials
* @returns DefaultAzureCredential
*/
function getCredentials() {
let credential = null

try {
let uamiCredentials = null
if (environment == "production" && azure_uami_clientId) {
uamiCredentials = new ManagedIdentityCredential(azure_uami_clientId)
if (azure_uami && azure_clientId) {
process.context.log("Loaded UAMI Client ID from Environment", azure_clientId)
credential = new ManagedIdentityCredential({
managedIdentityClientId: azure_clientId
})
return credential
}

return new ChainedTokenCredential(
uamiCredentials,
DefaultAzureCredential()
)
} catch (err) {
if (err.name == "RestError" && err.statusCode == 403) {
console.error(err.details.error.innerError)
} else {
console.error(err);
if (azure_tenantId, azure_clientId, azure_secret) {
process.context.log("Loaded Service Principal from Environment", azure_clientId)
credential = new ClientSecretCredential(azure_tenantId, azure_clientId, azure_secret)
return credential
}

if (environment == "development") {
process.context.log("Trying development credentials")
credential = new AzureDeveloperCliCredential()
return credential
}

process.context.log("Trying fallback credentials")
credential = new DefaultAzureCredential()
return credential
} catch (err) {
process.context.error("An error occurred handling the credentials", err)
return null
} finally {
process.context.log("Settled on using", credential)
}
}

Expand All @@ -63,7 +108,7 @@ async function getSubscriptions() {
const client = new SubscriptionClient(credentials);

if (azure_subscription && null !== azure_subscription) {
console.log("Loaded Subscription from Environment", azure_subscription)
process.context.log("Loaded Subscription from Environment", azure_subscription)
subscriptionsIds.push(await collect(client, azure_subscription))
} else {
for await (const item of client.subscriptions.list()) {
Expand All @@ -82,12 +127,11 @@ async function getSubscriptions() {
return { "subscriptionId": subscriptionId, "subscriptionName": displayName }
}
}
} catch (err) {
if (err.name == "RestError" && err.statusCode == 403) {
console.error(err.details.error.innerError)
} else {
console.error(err);
}
} catch (RestError) {
const err = JSON.parse(RestError.message).error
const status = RestError.statusCode

process.context.error("HTTP " + status, err.code, err.message)
}

return subscriptionsIds
Expand Down Expand Up @@ -122,9 +166,9 @@ async function getKeyVaults(subscription) {
}
} catch (err) {
if (err.name == "RestError" && err.statusCode == 403) {
console.error(err.details.error.innerError)
process.context.error(err.details.error.innerError)
} else {
console.error(err);
process.context.error(err);
}
}

Expand Down Expand Up @@ -174,7 +218,7 @@ async function getKeyVaultSecrets(keyVault) {
}
}
} catch (err) {
console.error(err);
process.context.error(err);
}

return secrets
Expand All @@ -190,7 +234,7 @@ function notifyOnExpiry(subscriptions) {
for (const subscription of subscriptions) {
const { subscriptionId, subscriptionName, keyVaults } = subscription

console.log("Processing Subscription", subscriptionName)
process.context.log("Processing Subscription", subscriptionName)

blocks.push({
type: "header",
Expand All @@ -211,10 +255,10 @@ function notifyOnExpiry(subscriptions) {
for (const keyVault of keyVaults) {
const { keyVaultName, keyVaultSecrets } = keyVault

console.log("Processing Key Vault", keyVaultName)
process.context.log("Processing Key Vault", keyVaultName)

if (keyVaultSecrets.length == 0) {
console.log("No secrets found for this Key Vault");
process.context.log("No secrets found for this Key Vault");
} else {
blocks.push({
type: "section",
Expand Down Expand Up @@ -247,7 +291,7 @@ function notifyOnExpiry(subscriptions) {
break;
}

console.log(message)
process.context.log(message)

childblocks.push({
text: secretName,
Expand All @@ -270,7 +314,7 @@ function notifyOnExpiry(subscriptions) {
}

if (null !== slack_webhook_url) {
console.log("Loaded Slack Webhook URL from Environment");
process.context.log("Loaded Slack Webhook URL from Environment");
const webhook = new IncomingWebhook(slack_webhook_url)

blocks.push({
Expand All @@ -294,6 +338,8 @@ function notifyOnExpiry(subscriptions) {
}

async function start() {
process.context.log("===== Beginning execution =====")

/**
* Query the authenticated user's identity for all available Subscriptions
*/
Expand All @@ -302,37 +348,42 @@ async function start() {
/**
* For each Subscription, Get a list of Key Vault IDs
*/
for (let subscription of subscriptions) {
const keyVaults = await getKeyVaults(subscription)
if (subscriptions.length) {
for (let subscription of subscriptions) {
const keyVaults = await getKeyVaults(subscription)

subscription.keyVaults = keyVaults
subscription.keyVaults = keyVaults

/**
* Once we have a list of Key Vaults for each Subscription
* then we can iterate through each one and query all of the Secrets
*/
for (let keyVault of subscription.keyVaults) {
const keyVaultSecrets = await getKeyVaultSecrets(keyVault)
/**
* Once we have a list of Key Vaults for each Subscription
* then we can iterate through each one and query all of the Secrets
*/
if (subscription.keyVaults.length) {
for (let keyVault of subscription.keyVaults) {
const keyVaultSecrets = await getKeyVaultSecrets(keyVault)

keyVault.keyVaultSecrets = keyVaultSecrets
keyVault.keyVaultSecrets = keyVaultSecrets
}
}
}
}

/**
* Check each Secret and output a message based on the state of the expiry
*/
notifyOnExpiry(subscriptions)
/**
* Check each Secret and output a message based on the state of the expiry
*/
notifyOnExpiry(subscriptions)
} else {
process.context.error("No Subscriptions were loaded")
}
}

/**
* Register a time-based invocation of the 'main' function
*/
app.timer('scheduledCronTrigger', {
schedule: '0 0 3 * * *',
handler: start
handler: init
});

/**
* Start the script
*/
start()
app.get('start', {
handler: init
})

0 comments on commit 1897723

Please sign in to comment.