I'm trying to use a timer trigger to run a refresh of an access token but for some reason the function is never triggered.
app.timer('RefreshAccessTokenTimer', {
schedule: '* * * * *',
handler: async (timer, context) => {
context.log('Timer function triggered:', timer.scheduleStatus);
context.log(`Schedule status: ${JSON.stringify(timer.scheduleStatus)}`);
// Retrieve tenantId, clientId, clientSecret, and refreshToken from secure storage
const tenantId = process.env.TENANT_ID;
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const refreshToken = process.env.REFRESH_TOKEN; // Update with your secure storage method
try {
const refreshedToken = await refreshAccessToken(tenantId, clientId, clientSecret, refreshToken);
context.log('Token refreshed successfully:', refreshedToken);
// Update the stored refresh token if necessary
process.env.REFRESH_TOKEN = refreshedToken.refreshToken;
process.env.ACCESS_TOKEN = refreshedToken.accessToken;
} catch (error) {
context.log.error('Error refreshing token:', error.message);
}
},
});
I ran it locally and saw my expected output but when I deploy it to azure I don't see any logs what so ever so I can't tell what's wrong. My deployment logs say that everything is fine as well.
I'm trying to use a timer trigger to run a refresh of an access token but for some reason the function is never triggered.
app.timer('RefreshAccessTokenTimer', {
schedule: '* * * * *',
handler: async (timer, context) => {
context.log('Timer function triggered:', timer.scheduleStatus);
context.log(`Schedule status: ${JSON.stringify(timer.scheduleStatus)}`);
// Retrieve tenantId, clientId, clientSecret, and refreshToken from secure storage
const tenantId = process.env.TENANT_ID;
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const refreshToken = process.env.REFRESH_TOKEN; // Update with your secure storage method
try {
const refreshedToken = await refreshAccessToken(tenantId, clientId, clientSecret, refreshToken);
context.log('Token refreshed successfully:', refreshedToken);
// Update the stored refresh token if necessary
process.env.REFRESH_TOKEN = refreshedToken.refreshToken;
process.env.ACCESS_TOKEN = refreshedToken.accessToken;
} catch (error) {
context.log.error('Error refreshing token:', error.message);
}
},
});
I ran it locally and saw my expected output but when I deploy it to azure I don't see any logs what so ever so I can't tell what's wrong. My deployment logs say that everything is fine as well.
Use a valid 6 digit CRON expression like 0 * * * * *
in your function code.
If your function is not getting triggered then you can check in the associated application insight to know about the error message if any.
I have used below code which worked for me locally as well in Azure function app.
const { app } = require('@azure/functions');
const axios = require('axios');
app.timer('RefreshAccessTokenTimer', {
schedule: '0 * * * * *',
handler: async (timer, context) => {
context.log('Timer function triggered:', timer.scheduleStatus);
context.log(`Schedule status: ${JSON.stringify(timer.scheduleStatus)}`);
const tenantId = process.env.TENANT_ID;
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const refreshToken = process.env.REFRESH_TOKEN;
try {
const refreshedToken = await refreshAccessToken(tenantId, clientId, clientSecret, refreshToken);
context.log('Token refreshed successfully:', refreshedToken);
process.env.REFRESH_TOKEN = refreshedToken.refreshToken;
process.env.ACCESS_TOKEN = refreshedToken.accessToken;
} catch (error) {
context.log.error('Error refreshing token:', error.message);
}
}
});
async function refreshAccessToken(tenantId, clientId, clientSecret, refreshToken) {
const tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
const params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('client_id', clientId);
params.append('client_secret', clientSecret);
params.append('refresh_token', refreshToken);
try {
const response = await axios.post(tokenEndpoint, params.toString(), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return {
accessToken: response.data.access_token,
refreshToken: response.data.refresh_token || refreshToken,
};
} catch (error) {
throw new Error(`Failed to refresh access token: ${error.response?.data?.error_description || error.message}`);
}
}
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "node",
"TENANT_ID": "932****af6d",
"CLIENT_ID": "c266****78ea5",
"CLIENT_SECRET": "_lg8Q*****bPu",
"REFRESH_TOKEN": "1.AX0AK*****7Z5g"
}
}
I am able to get expected result locally.
Azure Functions Core Tools
Core Tools Version: 4.0.6280 Commit hash: N/A +421f0144b42047aa289ce691dc6db4fc8b6143e6 (64-bit)
Function Runtime Version: 4.834.3.22875
[2025-01-07T06:38:34.375Z] Debugger listening on ws://12****5d43f1
[2025-01-07T06:38:34.376Z] For help, see: https://nodejs.org/en/docs/inspector
[2025-01-07T06:38:34.376Z] Debugger attached.
[2025-01-07T06:38:34.495Z] Worker process started and initialized.
Functions:
RefreshAccessTokenTimer: timerTrigger
For detailed output, run func with --verbose flag.
[2025-01-07T06:38:39.413Z] Host lock lease acquired by instance ID '0000000000000000000000000D2022A4'.
[2025-01-07T06:39:00.073Z] Executing 'Functions.RefreshAccessTokenTimer' (Reason='Timer fired at 2025-01-07T12:09:00.0403321+05:30', Id=0685fd5f-3322-48cc-b4ca-9cc1a90d006d)
[2025-01-07T06:39:00.180Z] Schedule status: {"last":"0001-01-01T00:00:00","next":"2025-01-07T12:09:00+05:30","lastUpdated":"0001-01-01T00:00:00"}
[2025-01-07T06:39:00.180Z] Timer function triggered: {
last: '0001-01-01T00:00:00',
next: '2025-01-07T12:09:00+05:30',
lastUpdated: '0001-01-01T00:00:00'
}
[2025-01-07T06:39:07.986Z] Token refreshed successfully: {
accessToken: 'eyJ0eXA******f2yqSu6jaIA',
refreshToken: '1.AX0AK*****5g1A'
}
[2025-01-07T06:39:08.010Z] Executed 'Functions.RefreshAccessTokenTimer' (Succeeded, Id=0685fd5f-3322-48cc-b4ca-9cc1a90d006d, Duration=7960ms)