├── FlowNotifications ├── AzureFunction │ ├── host.json │ ├── proxies.json │ ├── README.md │ ├── Format │ │ ├── function.json │ │ └── index.js │ ├── package.json │ └── package-lock.json ├── img │ ├── image001.jpg │ ├── image002.png │ ├── image003.png │ ├── image004.jpg │ ├── image005.jpg │ ├── image006.jpg │ ├── image007.jpg │ ├── image008.jpg │ ├── image009.jpg │ ├── image010.jpg │ ├── image011.jpg │ ├── image012.jpg │ ├── image013.jpg │ ├── image014.jpg │ ├── image015.jpg │ ├── image016.jpg │ ├── image017.jpg │ ├── image018.jpg │ ├── image019.jpg │ ├── image020.jpg │ ├── image021.jpg │ ├── image022.jpg │ ├── image023.jpg │ ├── image024.jpg │ ├── image025.jpg │ ├── image026.jpg │ ├── image027.jpg │ ├── image028.jpg │ ├── image029.jpg │ ├── image030.jpg │ ├── image031.jpg │ ├── image032.jpg │ ├── image033.jpg │ ├── image034.jpg │ ├── image035.jpg │ ├── image036.jpg │ ├── image037.jpg │ ├── image038.jpg │ ├── image039.jpg │ └── image040.jpg └── README.md ├── .gitignore ├── README.md └── OldFirewall ├── README.md └── Generate-OldFirewall-XML.ps1 /FlowNotifications/AzureFunction/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } 4 | -------------------------------------------------------------------------------- /FlowNotifications/AzureFunction/proxies.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/proxies", 3 | "proxies": {} 4 | } 5 | -------------------------------------------------------------------------------- /FlowNotifications/img/image001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image001.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image002.png -------------------------------------------------------------------------------- /FlowNotifications/img/image003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image003.png -------------------------------------------------------------------------------- /FlowNotifications/img/image004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image004.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image005.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image006.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image007.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image008.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image009.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image009.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image010.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image011.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image012.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image012.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image013.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image014.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image014.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image015.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image015.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image016.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image017.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image017.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image018.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image018.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image019.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image019.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image020.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image021.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image021.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image022.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image023.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image023.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image024.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image025.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image025.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image026.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image026.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image027.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image027.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image028.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image029.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image029.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image030.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image031.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image031.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image032.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image032.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image033.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image033.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image034.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image034.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image035.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image035.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image036.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image036.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image037.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image037.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image038.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image038.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image039.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image039.jpg -------------------------------------------------------------------------------- /FlowNotifications/img/image040.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandrew1/Office365-IPURL-Samples/HEAD/FlowNotifications/img/image040.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | FlowNotifications/AzureFunction/node_modules 3 | FlowNotifications/AzureFunction/.vscode 4 | FlowNotifications/AzureFunction/bin 5 | FlowNotifications/AzureFunction/Debug 6 | FlowNotifications/AzureFunction/obj 7 | -------------------------------------------------------------------------------- /FlowNotifications/AzureFunction/README.md: -------------------------------------------------------------------------------- 1 | # Azure Function for formatting JSON changes as HTML English text 2 | Sample code in JavaSCript for creating an Azure Function that generates HTML from the JSON changes web service output 3 | 4 | The project is arranged for downloading to a PC using Git and depoying to Azure using the Visual Studio Code extensions for Azure Functions. 5 | -------------------------------------------------------------------------------- /FlowNotifications/AzureFunction/Format/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "function", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "req", 8 | "methods": [ 9 | "get" 10 | ] 11 | }, 12 | { 13 | "type": "http", 14 | "direction": "out", 15 | "name": "res" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Office365-IPURL-Samples 2 | Sample code for connecting with the Office 365 IP Address and URL web services 3 | 4 | There are two projects 5 | 1) OldFirewall which generates the old XML file we used to publish from the new REST format 6 | 2) FlowNotifications which provides JavaScript code for an Azure Function to use used in a Microsoft flow that sends an email notification when there are changes to the IP/URL data 7 | -------------------------------------------------------------------------------- /FlowNotifications/AzureFunction/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "formatchanges", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "axios": "0.21.2" 6 | }, 7 | "main": "index.js", 8 | "scripts": { 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "Paul Andrew", 13 | "license": "ISC", 14 | "description": "Formats JSON output from changes web services in HTML for Ofice 365 IPURL updates", 15 | "devDependencies": {}, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/pandrew1/Office365-IPURL-Samples.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/pandrew1/Office365-IPURL-Samples/issues" 22 | }, 23 | "homepage": "https://github.com/pandrew1/Office365-IPURL-Samples#readme" 24 | } 25 | -------------------------------------------------------------------------------- /FlowNotifications/AzureFunction/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "formatchanges", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "axios": { 8 | "version": "0.21.2", 9 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz", 10 | "integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==", 11 | "requires": { 12 | "follow-redirects": "^1.14.0" 13 | } 14 | }, 15 | "follow-redirects": { 16 | "version": "1.15.2", 17 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 18 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OldFirewall/README.md: -------------------------------------------------------------------------------- 1 | # Office365-IPURL-Samples 2 | Sample code for connecting with the Office 365 IP Address and URL web services 3 | 4 | Generate-OldFirewall-XML.ps1 5 | This sample is for customers who have a dependency on one of the old XML downloads that was provided for Office 365. Notification was provided on 4/2/2018 that these would be deprecated and the files are no longer updated since 10/2/2018. There is a new JSON format output available with additional attributes from a web service as documented at http://aka.ms/ipurlblog. 6 | 7 | The script calls the new IP/URL web service passing in a parameter for the service instance requested and outputs the data in XML format similar to the previous downloads. The acceptable service instances are: 8 | * Worldwide 9 | * USGovDoD 10 | * USGovGCCHigh 11 | * China 12 | * Germany 13 | 14 | Compared to the old XML file, the product names will be different. THe new product names included are: 15 | * Exchange 16 | * SharePoint 17 | * Skype 18 | * Common 19 | 20 | We have some recommended migration approaches detailed here: https://techcommunity.microsoft.com/t5/Office-365-Networking/Migrating-to-the-new-web-services-based-publishing-for-Office/m-p/229144 21 | -------------------------------------------------------------------------------- /OldFirewall/Generate-OldFirewall-XML.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # Script loads Office 365 endpoints from web service, orders them according to the XML schema, and outputs a file 3 | # 4 September 2018 4 | # 5 | # Copyright Microsoft 2018 6 | # 7 | 8 | # Set parameters for the script 9 | $wsRoot = "https://endpoints.office.com" 10 | $clientRequestId = "b10c5ed1-bad1-445f-b386-b919946339a7" 11 | $instanceName = "Worldwide" 12 | $outfilePath = "O365IPAddresses_$instanceName.xml" 13 | 14 | # Get latest endpoints data from the web service 15 | $e = Invoke-RestMethod -Uri ("$($wsRoot)/endpoints/$($instanceName)?clientrequestid=$($clientRequestId)") 16 | 17 | # Structure attributes and split IPv4 and IPv6 18 | $canon = $e | ForEach-Object { 19 | $ipv4 = @() 20 | $ipv6 = @() 21 | 22 | if ("ips" -in $_.PSobject.properties.name) { 23 | $ipv4 = $_.ips | Where-Object {$_ -like '*.*'} 24 | $ipv6 = $_.ips | Where-Object {$_ -like '*:*'} 25 | } 26 | 27 | [PSCustomObject]@{ 28 | id = $_.id; 29 | product = $_.serviceArea; 30 | ipv4 = $ipv4; 31 | ipv6 = $ipv6; 32 | urls = $_.urls; 33 | } 34 | } 35 | 36 | # Order of the workload keys in the published XML 37 | $workloadKeysOrdered = @("Exchange","SharePoint","Skype","Common") 38 | 39 | # Helper func to sort IPs numerically 40 | $sortIPFunc = { 41 | $byteArray = [System.Net.IPAddress]::Parse($_.Split('/')[0]).GetAddressBytes() 42 | [System.Array]::Reverse($byteArray) 43 | if ($byteArray.Count -eq 16) { 44 | # IPv6 - convert to 4-element 32-bit int array and then to string 45 | (@(12, 8, 4, 0) | foreach { '{0:x8}' -f [System.BitConverter]::ToUInt32($byteArray, $_) }) -join "" 46 | } 47 | else { 48 | # IPv4 - converting to 32-bit int is sufficient 49 | [System.BitConverter]::ToUInt32($byteArray, 0) 50 | } 51 | } 52 | 53 | # Output XML 54 | $xml = "`r`n" 55 | $xml += "`r`n" 56 | $xml += $(($workloadKeysOrdered | foreach { 57 | $workloadName = $_ 58 | $mergeObject = $canon | where { $_.product -eq $workloadName } 59 | 60 | " `r`n" + 61 | 62 | # IPv6 63 | $(if ($mergeObject.ipv6.Length -gt 0) { 64 | " `r`n" + 65 | $(($mergeObject.ipv6 | Sort -Unique -Property $sortIPFunc | ForEach-Object { 66 | "
$_
`r`n" 67 | }) -join "") + 68 | "
`r`n" 69 | } else { "" }) + 70 | 71 | # IPv4 72 | $(if ($mergeObject.ipv4.Length -gt 0) { 73 | " `r`n" + 74 | $(($mergeObject.ipv4 | Sort -Unique -Property $sortIPFunc | ForEach-Object { 75 | "
$_
`r`n" 76 | }) -join "") + 77 | "
`r`n" 78 | } else { "" }) + 79 | 80 | # URL 81 | $(if ($mergeObject.urls.Length -gt 0) { 82 | " `r`n" + 83 | $(($mergeObject.urls | Sort -Unique | ForEach-Object { 84 | "
$_
`r`n" 85 | }) -join "") + 86 | "
`r`n" 87 | } else { "" }) + 88 | 89 | "
" 90 | 91 | }) -join "`r`n") 92 | $xml += '
' 93 | 94 | $xml | Out-File $outfilePath 95 | -------------------------------------------------------------------------------- /FlowNotifications/AzureFunction/Format/index.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | module.exports = async function (context, req) { 4 | if (req.query.since && req.query.instance && req.query.clientrequestid && req.query.name) { 5 | context.log('JavaScript HTTP trigger function processing a request for ' + req.query.name); 6 | var url = "http://endpoints.office.com/changes/" + req.query.instance + "/" + req.query.since + "?clientrequestid=" + req.query.clientrequestid; 7 | var response = await axios.get(url); 8 | var changes = JSON.parse(JSON.stringify(response.data)) 9 | context.res = { 10 | headers: { 11 | "Content-Type": "text/html" 12 | }, 13 | body: "Table of changes" + 14 | "" + 17 | "

Recent changes for Office 365 IP Addresses and URLs. For more information please see http://aka.ms/ipurlws

" + 18 | formatChanges(changes) + "" 19 | }; 20 | } else { 21 | context.log('JavaScript HTTP trigger function processed a request with an invalid parameters.'); 22 | context.res = { 23 | status: 400, 24 | body: "Please pass query string parameters since, instance, clientrequestid, and key. Docs at http://aka.ms/ipurlws" 25 | }; 26 | } 27 | } 28 | 29 | function formatChanges(changes) { 30 | var out = "" 31 | changes.forEach(function(obj) { 32 | out += "

Change {0}

".replace("{0}", obj.id) 33 | out += "

Endpoint Set ID: " + obj.endpointSetId 34 | out += ", Disposition: " + obj.disposition 35 | out += ", Version " + obj.version 36 | if (isdef(obj.impact)) { 37 | out += ", Impact: " + obj.impact 38 | } 39 | out += "

" 40 | if (isdef(obj.previous)) { 41 | out += "" + formatSub(obj.previous) 42 | } 43 | if (isdef(obj.current)) { 44 | out += "" + formatSub(obj.current) 45 | } 46 | if (isdef(obj.add)) { 47 | out += "" 71 | } else if (isdef(obj.remove)) { 72 | out += "" 93 | } 94 | out += "
Previous
Current
Add
" 48 | if (isdef(obj.add.effectiveDate)) { 49 | out += "Effective Date: {0} ".replace("{0}", obj.add.effectiveDate) 50 | } 51 | if (isdef(obj.add.urls)) { 52 | out += "URLs: " 53 | var first = true 54 | obj.add.urls.forEach(function(url) { 55 | if (!first) { out += ", " } 56 | out += url 57 | first = false 58 | }) 59 | out += " " 60 | } 61 | if (isdef(obj.add.ips)) { 62 | out += "IP Addresses: " 63 | var first = true 64 | obj.add.ips.forEach(function(url) { 65 | if (!first) { out += ", "} 66 | out += url 67 | first = false 68 | }) 69 | } 70 | out += "
Remove
" 73 | if (isdef(obj.remove.urls)) { 74 | out += "URLs: " 75 | var first = true 76 | obj.remove.urls.forEach(function(url) { 77 | if (!first) { out += ", "} 78 | out += url 79 | first = false 80 | }) 81 | out += " " 82 | } 83 | if (isdef(obj.remove.ips)) { 84 | out += "IP Addresses: " 85 | var first = true 86 | obj.remove.ips.forEach(function(url) { 87 | if (!first) { out += ", "} 88 | out += url 89 | first = false 90 | }) 91 | } 92 | out += "
" 95 | }); 96 | return out; 97 | } 98 | 99 | function formatSub(node){ 100 | var out = "" 101 | var first = true 102 | if (isdef(node.expressRoute)) { 103 | if (!first) { out += ", "}; first = false 104 | out += "ExpressRoute: " + node.expressRoute 105 | } 106 | if (isdef(node.serviceArea)) { 107 | if (!first) { out += ", "}; first = false 108 | out += "serviceArea: " + node.serviceArea 109 | } 110 | if (isdef(node.category)) { 111 | if (!first) { out += ", "}; first = false 112 | out += "category: " + node.category 113 | } 114 | if (isdef(node.notes)) { 115 | if (!first) { out += ", "}; first = false 116 | out += "notes: " + node.notes 117 | } 118 | if (isdef(node.required)) { 119 | if (!first) { out += ", "}; first = false 120 | out += "required: " + node.required 121 | } 122 | if (isdef(node.tcpPorts)) { 123 | if (!first) { out += ", "}; first = false 124 | out += "tcpPorts: " + node.tcpPorts 125 | } 126 | if (isdef(node.udpPorts)) { 127 | if (!first) { out += ", "}; first = false 128 | out += "udpPorts: " + node.udpPorts 129 | } 130 | out += "" 131 | return out; 132 | } 133 | 134 | function isdef(node){ 135 | return (typeof node !== 'undefined'); 136 | } -------------------------------------------------------------------------------- /FlowNotifications/README.md: -------------------------------------------------------------------------------- 1 | # Creating a Microsoft Flow to email yourself when an Office 365 IP/URL change occurs 2 | 3 | This article is an updated method for demonstrating how you can use Microsoft Flow to alert yourself with an email whenever there are changes to the Office 365 IP Addresses or URLs. This improves on the previous Flow by removing the requirement for a SharePoint Online list to be created for state management and by including English formatted text in the email body describing the changes instead of the previous JSON file attachments. 4 | 5 | I've divided this article into two parts since the first part is much simpler than the second. The first part is to create the flow and get an email notification and the second part is to format the change list in English text instead of JSON. 6 | 7 | The Flow created in this article is not supported by Microsoft and you should follow your own development review processes before relying on it in a production environment. 8 | 9 | ![alt text](img/image001.jpg "Figure 1") 10 | 11 | _Figure 1 – Completed change notification email result_ 12 | 13 | ## Step 1 – Sign up for Microsoft Flow 14 | 15 | Flow requires sign-up. I've only used free elements in Flow for this. You can read about the sign-up process and the free and paid plans at [https://docs.microsoft.com/en-us/flow/sign-up-sign-in](https://docs.microsoft.com/en-us/flow/sign-up-sign-in) 16 | 17 | Once you've signed up you can go to flow at [https://flow.microsoft.com](https://flow.microsoft.com/) 18 | 19 | ## Step 2 – Create a flow 20 | 21 | At the flow home page, select My Flows from the left side navigation menu. On the My Flows page you can select + New at the top of the page and select Create from blank to create your flow. 22 | 23 | ![alt text](img/image002.png "Figure 2") 24 | 25 | _Figure 2 - Create from blank command_ 26 | 27 | ## Step 3 – Add the trigger 28 | 29 | A trigger starts your flow executing. We're going to check the version of the Office 365 network endpoints using the RSS feed. 30 | 31 | The RSS feed trigger is not very easy to test so you will want to use the Recurrence trigger for testing so that you get a trigger for the flow once a minute. 32 | 33 | Let's keep going with the RSS trigger here. You can delete it and add the Recurrence to test and then delete your Recurrence and add an RSS trigger back if testing and debugging is needed. 34 | 35 | ![alt text](img/image003.png "Figure 3") 36 | 37 | _Figure 3 - Search triggers command_ 38 | 39 | Click the search as shown in Figure 2 and enter RSS in the search box. It will begin searching immediately and the orange RSS trigger should appear. Select it. 40 | 41 | There's one parameter for the RSS trigger which is the URL to look up. This URL will be custom for the service instance that you want to monitor. You can use this URL to get the RSS feed for the most commonly used service instance. It's called Office 365 Worldwide Commercial/GCC. 42 | 43 | https://endpoints.office.com/version/worldwide?format=rss&AllVersions&clientrequestid=bad1f103-bad1-f103-0123-456789abcdef 44 | 45 | ![alt text](img/image004.jpg "Figure 4") 46 | 47 | _Figure 4 - Configured RSS trigger_ 48 | 49 | ## Step 4 – Adding the actions 50 | 51 | Then click the + New step command and choose Add an action. 52 | 53 | ![alt text](img/image005.jpg "Figure 5") 54 | 55 | _Figure 5 – New step command_ 56 | 57 | Search for the HTTP action and select it. It's the first on the result list on figure 5. 58 | 59 | ![alt text](img/image006.jpg "Figure 6") 60 | 61 | _Figure 6 – New step command_ 62 | 63 | Configure the HTTP action to GET the version list for the endpoints from the web service. 64 | 65 | https://endpoints.office.com/version/worldwide?allversions&clientrequestid=bad1f103-bad1-f103-0123-456789abcdef 66 | 67 | ![alt text](img/image007.jpg "Figure 7") 68 | 69 | _Figure 7 – Parameters for GET HTTP action_ 70 | 71 | Click the + New step command below the HTTP action and search for JSON to locate the Parse JSON action. 72 | 73 | ![alt text](img/image008.jpg "Figure 8") 74 | 75 | _Figure 8 – Action search results showing the Parse JSON action_ 76 | 77 | Configure the Parse JSON parameters to read the results from the web service. 78 | 79 | ![alt text](img/image009.jpg "Figure 9") 80 | 81 | _Figure 9 – Configuring the Parse JSON action_ 82 | 83 | Click in the Content property window and you will see the dynamic content selector. Choose the Body content item which is the output of the previous action. 84 | 85 | ![alt text](img/image010.jpg "Figure 10") 86 | 87 | _Figure 10 – Selecting the Body content item_ 88 | 89 | Next, we are going to setup the schema so that the Parse JSON action can read the web service output. Launch a web browser and paste in the 90 | 91 | ![alt text](img/image011.jpg "Figure 11") 92 | 93 | _Figure 11 – Getting a sample output payload for the web service._ 94 | 95 | Click use sample payload to generate schema. 96 | 97 | ![alt text](img/image012.jpg "Figure 12") 98 | 99 | _Figure 12 – Getting a sample output payload for the web service._ 100 | 101 | ![alt text](img/image013.jpg "Figure 13") 102 | 103 | _Figure 13 - Configured Parse JSON action_ 104 | 105 | Add a second HTTP action and configure it for a GET operation. This second HTTP action is to get the list of changes that were published in the latest update. We will configure this to get the JSON format data and include that in the notification email first. Later we will come back and update this HTTP action to get English formatted text describing the changes. Those are added later because there is more work to create them than just to include the JSON format data. 106 | 107 | The URI for this HTTP GET is going to be more complex than in the first HTTP GET action as it includes a dynamic parameter. Enter the first part of the URI as: 108 | 109 | https://endpoints.office.com/changes/worldwide/ 110 | 111 | Then click Expression in the expanded right properties window and enter a reference to the second version item from the Parse JSON action. This selects the version prior to the current version so that you can see the latest changes. 112 | 113 | **body(**'Parse\_JSON'**)**?['versions'][1] 114 | 115 | ![alt text](img/image014.jpg "Figure 14") 116 | 117 | _Figure 14 – First part of URI entered_ 118 | 119 | Click OK to accept the Expression and then enter the second part of the URI. 120 | 121 | ?clientrequestid=bad1f103-bad1-f103-0123-456789abcdef 122 | 123 | ![alt text](img/image015.jpg "Figure 15") 124 | 125 | _Figure 15 – Expression and second part of URI entered_ 126 | 127 | Select + New step and search for the Office 365 Send an email action. 128 | 129 | ![alt text](img/image016.jpg "Figure 16") 130 | 131 | _Figure 16 – Searching for the send an email action_ 132 | 133 | Select the Send an email (V2) action. 134 | 135 | Configure the Send an email action with your own email to address. Enter a subject line in two parts. The first part is static: 136 | 137 | New Office 365 IPURL changes were published 138 | 139 | The second part of the subject will be the version number that we just found. With the cursor at the end of the subject text, select the Expression tab in the expanded right properties window and enter the expression to reference the latest version in the JSON output from the version web service. 140 | 141 | **body(**'Parse\_JSON'**)**?['latest'] 142 | 143 | 144 | ![alt text](img/image017.jpg "Figure 17") 145 | 146 | _Figure 17 – Searching for the send an email action_ 147 | 148 | Next select OK to enter the expression into the Subject and do the same thing for the Body. Enter some static text for the Body. 149 | 150 | "Changes to Office 365 IP Address and/or URLs have been published. Here is the change log." 151 | 152 | ![alt text](img/image018.jpg "Figure 18") 153 | 154 | _Figure 18 – The email action with the subject complete and the static text entered in the body_ 155 | 156 | Select the HTTP 2 action Body for the rest of the email body. 157 | 158 | ![alt text](img/image019.jpg "Figure 19") 159 | 160 | _Figure 19 – The completed email action. Please substitute your own email address_ 161 | 162 | You are complete and can now save the flow. 163 | 164 | ![alt text](img/image020.jpg "Figure 20") 165 | 166 | _Figure 20 – The save button at the bottom of the flow_ 167 | 168 | ![alt text](img/image021.jpg "Figure 21") 169 | 170 | _Figure 21 – The completed flow_ 171 | 172 | 173 | 174 | ## Step 5 – Testing and troubleshooting the flow 175 | 176 | The flow only triggers when a new RSS article is published, and this only occurs one or two times a month so it's important to be able to have some means to test the flow. To do this we will delete the RSS trigger and replace it with a Recurrence trigger that fires every minute. If the flow is working, you will get an email every minute due to the Recurrence firing and that email will include the most recent changes. When you have this working, delete the Recurrence trigger and put back the RSS trigger. 177 | 178 | ![alt text](img/image022.jpg "Figure 22") 179 | 180 | _Figure 22 – The menu to delete the RSS trigger_ 181 | 182 | Select Delete. Once this is deleted you will be prompted to add a new trigger. Enter Recurrence in the search box and select the Recurrence Schedule activity. 183 | 184 | ![alt text](img/image023.jpg "Figure 23") 185 | 186 | _Figure 23 – Adding the schedule trigger_ 187 | 188 | The default properties for the schedule trigger are to fire the flow once a minute. Leave these as is and save the flow. You should receive an email within a minute containing the latest changes. Now you can observe the flow executing, look at data accessed by each activity and do any needed troubleshooting in execution logs. 189 | 190 | ## Step 6 – Creating an Azure Function for formatting 191 | 192 | To create emails with English formatted changes we will use an Azure Function written in JavaScript which calls the changes web service and creates the English formatted output. 193 | 194 | Before creating an Azure Function it would be helpful to read the Azure Function JavaScript documentation here: [https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node](https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node) 195 | 196 | Go to the Azure Portal at [http://portal.azure.com](http://portal.azure.com) 197 | 198 | Sign-in with your Azure subscription account. 199 | 200 | Click Create a resource and type "function" in the search box. 201 | 202 | ![alt text](img/image024.jpg "Figure 24") 203 | 204 | _Figure 24 – Searching for the Azure Function App_ 205 | 206 | Choose Function App from the search results list. 207 | 208 | ![alt text](img/image025.jpg "Figure 25") 209 | 210 | _Figure 25 – Creating a Function App_ 211 | 212 | Select the Create link. Enter your own selected unique name for the Function App in the App name field, change the Runtime stack to JavaScript, edit any other properties you need, and click the create button. 213 | 214 | ![alt text](img/image026.jpg "Figure 26") 215 | 216 | _Figure 26 – Entering properties for the new Azure Function App_ 217 | 218 | Deployment will take a minute or two. Once it is complete you will get a notification or you can choose App Services from the left navigation menu to see it. 219 | 220 | ![alt text](img/image027.jpg "Figure 27") 221 | 222 | _Figure 27 – The deployed Azure Function App_ 223 | 224 | Select functions on the Function App and select + New function. 225 | 226 | ![alt text](img/image028.jpg "Figure 28") 227 | 228 | _Figure 28 – The New function button_ 229 | 230 | Select the HTTP Trigger. 231 | 232 | ![alt text](img/image029.jpg "Figure 29") 233 | 234 | _Figure 29 – Select the HTTP trigger_ 235 | 236 | Enter the name as format and use the default Authorization level of Function. This means that you will be required to pass a code parameter with the function key each time you call the function. 237 | 238 | ![alt text](img/image030.jpg "Figure 30") 239 | 240 | _Figure 30 – Configure the HTTP trigger_ 241 | 242 | Now you will see a template JavaScript function. Next, we're going to use Visual Studio Code to edit and deploy the function. 243 | 244 | I'm using it on Windows. Get Visual Studio Code for Windows here: [https://code.visualstudio.com/download](https://code.visualstudio.com/download) 245 | 246 | Get the Azure Function App extension here: [https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions) 247 | 248 | As described on the extension page, you will need to install npm and the Azure Functions module. Npm is installed with Node.js from https://nodejs.org/en/download/. 249 | 250 | npm i -g azure-functions-core-tools@2 251 | 252 | You also need to install git. It can be obtained here: 253 | 254 | [https://git-scm.com/download/win](https://git-scm.com/download/win) 255 | 256 | Choose a home directory on your local machine and clone the git repository for the Azure Function code. 257 | 258 | At the git command prompt type: 259 | 260 | git clone [https://github.com/pandrew1/Office365-IPURL-Samples](https://github.com/pandrew1/Office365-IPURL-Samples) 261 | 262 | Using your npm prompt change to the AzureFunction directory where the file package.json is located and fetch the dependencies with: 263 | 264 | npm install 265 | 266 | Start Visual Studio Code and open the folder with the Azure Function as a workspace. 267 | 268 | ![alt text](img/image031.jpg "Figure 31") 269 | 270 | _Figure 31 – Configure the HTTP trigger_ 271 | 272 | Choose the folder called AzureFunction where the file package.json is located. 273 | 274 | You can test locally in Visual Studio Code by pressing F5. Check the Terminal window for the test URL on your local machine. 275 | 276 | To deploy to Azure, select the Azure integration icon at the bottom of the let navigation menu. 277 | 278 | ![alt text](img/image032.jpg "Figure 32") 279 | 280 | _Figure 32 – Left navigation menu in Visual Studio Code_ 281 | 282 | ![alt text](img/image033.jpg "Figure 33") 283 | 284 | _Figure 33 – Top navigation menu for Azure in Visual Studio Code_ 285 | 286 | Select Deploy to Function App… which is the arrow pointing up to a line. 287 | 288 | Select your previously created Azure Function App for the deployment. 289 | 290 | ![alt text](img/image034.jpg "Figure 34") 291 | 292 | _Figure 34 – Overwrite dialog_ 293 | 294 | Go back to the Azure Portal and view the function that you just published. It should already be running. You can test it in the portal, and you can get the URL to it. 295 | 296 | ![alt text](img/image035.jpg "Figure 35") 297 | 298 | _Figure 35 – Deployed Azure Function_ 299 | 300 | You can use the test panel on the right after entering the required parameters. Scroll to the bottom of the panel to find the Run button that runs the test. 301 | 302 | ![alt text](img/image036.jpg "Figure 36") 303 | 304 | _Figure 36 – Test parameters_ 305 | 306 | After running the test you should see Status: 200 OK and the resulting HTML. 307 | 308 | ![alt text](img/image037.jpg "Figure 37") 309 | 310 | _Figure 37 – A successful test_ 311 | 312 | Now get the URL for your Azure Function. This will include your function key. Select Get Function URL. 313 | 314 | ![alt text](img/image038.jpg "Figure 38") 315 | 316 | _Figure 38 – A successful test_ 317 | 318 | Here's what it looks like: 319 | 320 | https://a-unique-name.azurewebsites.net/api/Format?code=pUDGdBY4yxeLOdAJB8LX3nuuuQ2T/xG0/xnzWhoBy0XgU4pjzqbURA== 321 | 322 | ## Step 7 – Updating the flow to use the formatting function 323 | 324 | Now the you have an Azure Function working let's go back and update the Flow to call it. Here is the current HTTP 2 activity. 325 | 326 | ![alt text](img/image039.jpg "Figure 39") 327 | 328 | _Figure 39 – The HTTP 2 activity to be updated_ 329 | 330 | We're going to replace the URI with the URI to your new Azure Function. We will keep the Flow expression that identifies the previous version number but update the first static part of the URI. The first part should point to your Azure Function URL. Here is my test one, but you need to use your own URL and your own code. The second static part of the URI can be removed. 331 | 332 | https://a-unique-name.azurewebsites.net/api/format?code=pUDGdBY4yxeLOdAJB8LX3nuuuQ2T/xG0/xnzWhoBy0XgU4pjzqbURA==&name=paul&instance=worldwide&clientrequestid=bad1f103-bad1-f103-0123-456789abcdef&since= 333 | 334 | ![alt text](img/image040.jpg "Figure 40") 335 | 336 | _Figure 40 – The updated HTTP 2 activity_ 337 | 338 | # 339 | # Summary 340 | 341 | A simple Microsoft Flow can make the IP/URL change publishing easy to distribute and review. 342 | 343 | You could extend this Flow with approvals as needed and forward the changes to your team who manages network perimeter updates. 344 | --------------------------------------------------------------------------------