Created
July 1, 2025 09:01
-
-
Save maesoser/c43a10ae665dfd0757ef38c2de86d16b to your computer and use it in GitHub Desktop.
Cloudflare Workers script that fetches Microsoft Azure Service Tags JSON, extracts IP addresses for a specified service (like ApplicationInsightsAvailability), and automatically updates a Cloudflare IP list via API.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Cloudflare Workers script to update IP lists from Microsoft Service Tags | |
// Configure these environment variables in your worker: | |
// - CLOUDFLARE_API_KEY: Your Cloudflare API key | |
// - CLOUDFLARE_ACCOUNT_ID: Your Cloudflare account ID | |
// - CLOUDFLARE_LIST_ID: The ID of the list to update | |
// - SERVICE_NAME: The service name to filter (e.g., "ApplicationInsightsAvailability") | |
const MICROSOFT_SERVICE_TAGS_URL = 'https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250616.json'; | |
export default { | |
async updateList(serviceName, env){ | |
console.log(`Processing service: ${serviceName}`); | |
// Fetch Microsoft Service Tags JSON | |
const serviceTagsResponse = await fetch(MICROSOFT_SERVICE_TAGS_URL); | |
if (!serviceTagsResponse.ok) { | |
throw new Error(`Failed to fetch service tags: ${serviceTagsResponse.status}`); | |
} | |
const serviceTagsData = await serviceTagsResponse.json(); | |
console.log(`Fetched service tags with change number: ${serviceTagsData.changeNumber}`); | |
// Find the specified service in the values array | |
const targetService = serviceTagsData.values.find(service => service.name === serviceName); | |
if (!targetService) { | |
return new Response( | |
JSON.stringify({ | |
error: `Service '${serviceName}' not found`, | |
availableServices: serviceTagsData.values.map(s => s.name).slice(0, 10) // Show first 10 for reference | |
}, null, 2), | |
{ | |
status: 404, | |
headers: { 'Content-Type': 'application/json' } | |
} | |
); | |
} | |
// Extract IP addresses from the service | |
const ipAddresses = targetService.properties.addressPrefixes || []; | |
console.log(`Found ${ipAddresses.length} IP addresses for ${serviceName}`); | |
if (ipAddresses.length === 0) { | |
return new Response( | |
JSON.stringify({ | |
message: `No IP addresses found for service '${serviceName}'`, | |
service: targetService | |
}, null, 2), | |
{ | |
status: 200, | |
headers: { 'Content-Type': 'application/json' } | |
} | |
); | |
} | |
// Prepare items for Cloudflare list update | |
const listItems = ipAddresses.map(ip => ({ | |
ip: ip, | |
comment: `${serviceName} - Updated from Microsoft Service Tags (Change #${serviceTagsData.changeNumber})` | |
})); | |
// Update Cloudflare list | |
const cloudflareApiUrl = `https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/rules/lists/${env.CLOUDFLARE_LIST_ID}/items`; | |
const cloudflareResponse = await fetch(cloudflareApiUrl, { | |
method: 'PUT', | |
headers: { | |
'Content-Type': 'application/json', | |
'Authorization': `Bearer ${env.CLOUDFLARE_API_KEY}`, | |
}, | |
body: JSON.stringify(listItems) | |
}); | |
const cloudflareResult = await cloudflareResponse.json(); | |
if (!cloudflareResponse.ok) { | |
console.error('Cloudflare API error:', cloudflareResult); | |
return new Response( | |
JSON.stringify({ | |
error: 'Failed to update Cloudflare list', | |
details: cloudflareResult | |
}, null, 2), | |
{ | |
status: cloudflareResponse.status, | |
headers: { 'Content-Type': 'application/json' } | |
} | |
); | |
} | |
// Return success response | |
return new Response( | |
JSON.stringify({ | |
success: true, | |
message: `Successfully updated Cloudflare list with ${ipAddresses.length} IP addresses`, | |
service: serviceName, | |
changeNumber: serviceTagsData.changeNumber, | |
ipCount: ipAddresses.length, | |
listId: env.CLOUDFLARE_LIST_ID, | |
cloudflareResponse: cloudflareResult | |
}, null, 2), | |
{ | |
status: 200, | |
headers: { | |
'Content-Type': 'application/json', | |
'Access-Control-Allow-Origin': '*' | |
} | |
} | |
); | |
}, | |
async fetch(request, env, ctx) { | |
try { | |
// Handle CORS preflight requests | |
if (request.method === 'OPTIONS') { | |
return new Response(null, { | |
headers: { | |
'Access-Control-Allow-Origin': '*', | |
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', | |
'Access-Control-Allow-Headers': 'Content-Type', | |
}, | |
}); | |
} | |
// Only allow GET and POST requests | |
if (!['GET', 'POST'].includes(request.method)) { | |
return new Response('Method not allowed', { status: 405 }); | |
} | |
// Validate required environment variables | |
const requiredEnvVars = [ 'CLOUDFLARE_API_KEY', 'CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_LIST_ID']; | |
const missingVars = requiredEnvVars.filter(varName => !env[varName]); | |
if (missingVars.length > 0) { | |
return new Response( | |
JSON.stringify({ | |
error: 'Missing required environment variables', | |
missing: missingVars | |
}, null, 2), | |
{ | |
status: 500, | |
headers: { 'Content-Type': 'application/json' } | |
} | |
); | |
} | |
// Get service name from query parameter or environment variable | |
const url = new URL(request.url); | |
const serviceName = url.searchParams.get('service') || env.SERVICE_NAME || 'ApplicationInsightsAvailability'; | |
return this.updateList(serviceName, env); | |
} catch (error) { | |
console.error('Error in worker:', error); | |
return new Response( | |
JSON.stringify({ | |
error: 'Internal server error', | |
message: error.message | |
}, null, 2), | |
{ | |
status: 500, | |
headers: { 'Content-Type': 'application/json' } | |
} | |
); | |
} | |
}, | |
// Optional: Add a scheduled handler for automatic updates | |
async scheduled(controller, env, ctx) { | |
try { | |
// Create a fake request to trigger the main logic | |
const requiredEnvVars = [ 'CLOUDFLARE_API_KEY', 'CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_LIST_ID']; | |
const missingVars = requiredEnvVars.filter(varName => !env[varName]); | |
if (missingVars.length > 0) { | |
return new Response( | |
JSON.stringify({ | |
error: 'Missing required environment variables', | |
missing: missingVars | |
}, null, 2), | |
{ | |
status: 500, | |
headers: { 'Content-Type': 'application/json' } | |
} | |
); | |
} | |
// Get service name from query parameter or environment variable | |
const serviceName = env.SERVICE_NAME || 'ApplicationInsightsAvailability'; | |
console.error(this.updateList(serviceName, env)); | |
} catch (error) { | |
console.error('Scheduled update failed:', error); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment