Hi all,
Attached a Pipeline to generate M365 Host Objects in SFOS with Sophos Factory.
You need two Pipelines: One Child Pipeline and one Parent Pipeline:
Get-O365WebserviceUpdates Pipeline:
--- variables: - type: Credential name: firewallcreds key: firewallcreds value: FirewallCreds required: false visible: true default: false allowed_types: - username_password - type: String name: firewallhostname key: firewallhostname value: saleseng.de required: false visible: true default: false steps: - id: GetO365 name: GetO365 type: powershell_script depends: - powershell_install1 properties: content: |- <# Get-O365WebServiceUpdates.ps1 From https://aka.ms/ipurlws v1.1 8/6/2019 DESCRIPTION This script calls the REST API of the Office 365 IP and URL Web Service (Worldwide instance) and checks to see if there has been a new update since the version stored in an existing $Env:TEMP\O365_endpoints_latestversion.txt file in your user directory's temp folder (usually C:\Users\<username>\AppData\Local\Temp). If the file doesn't exist, or the latest version is newer than the current version in the file, the script returns IPs and/or URLs that have been changed, added or removed in the latest update and writes the new version and data to the output file $Env:TEMP\O365_endpoints_data.txt. USAGE Run as a scheduled task every 60 minutes. PARAMETERS n/a PREREQUISITES PS script execution policy: Bypass PowerShell 3.0 or later Does not require elevation #> #Requires -Version 3.0 # web service root URL $ws = "https://endpoints.office.com" # path where output files will be stored $versionpath = $Env:TEMP + "\O365_endpoints_latestversion.txt" $datapath = $Env:TEMP + "\O365_endpoints_data.txt" # fetch client ID and version if version file exists; otherwise create new file and client ID if (Test-Path $versionpath) { $content = Get-Content $versionpath $clientRequestId = $content[0] $lastVersion = $content[1] # Write-Output ("Version file exists! Current version: " + $lastVersion) } else { # Write-Output ("First run! Creating version file at " + $versionpath + ".") $clientRequestId = [GUID]::NewGuid().Guid $lastVersion = "0000000000" @($clientRequestId, $lastVersion) | Out-File $versionpath } # call version method to check the latest version, and pull new data if version number is different $version = Invoke-RestMethod -Uri ($ws + "/version/Worldwide?clientRequestId=" + $clientRequestId) if ($version.latest -gt $lastVersion) { # Write-Host "New version of Office 365 worldwide commercial service instance endpoints detected" # write the new version number to the version file @($clientRequestId, $version.latest) | Out-File $versionpath # invoke endpoints method to get the new data $endpointSets = Invoke-RestMethod -Uri ($ws + "/endpoints/Worldwide?clientRequestId=" + $clientRequestId) # filter results for Allow and Optimize endpoints, and transform these into custom objects with port and category # URL results $flatUrls = $endpointSets | ForEach-Object { $endpointSet = $_ $urls = $(if ($endpointSet.urls.Count -gt 0) { $endpointSet.urls } else { @() }) $urlCustomObjects = @() if ($endpointSet.category -in ("Allow", "Optimize")) { $urlCustomObjects = $urls | ForEach-Object { [PSCustomObject]@{ category = $endpointSet.category; url = $_; tcpPorts = $endpointSet.tcpPorts; udpPorts = $endpointSet.udpPorts; } } } $urlCustomObjects } # IPv4 results $flatIp4s = $endpointSets | ForEach-Object { $endpointSet = $_ $ips = $(if ($endpointSet.ips.Count -gt 0) { $endpointSet.ips } else { @() }) # IPv4 strings contain dots $ip4s = $ips | Where-Object { $_ -like '*.*' } $ip4CustomObjects = @() if ($endpointSet.category -in ("Allow", "Optimize")) { $ip4CustomObjects = $ip4s | ForEach-Object { [PSCustomObject]@{ category = $endpointSet.category; ip = $_; tcpPorts = $endpointSet.tcpPorts; udpPorts = $endpointSet.udpPorts; } } } $ip4CustomObjects } # IPv6 results $flatIp6s = $endpointSets | ForEach-Object { $endpointSet = $_ $ips = $(if ($endpointSet.ips.Count -gt 0) { $endpointSet.ips } else { @() }) # IPv6 strings contain colons $ip6s = $ips | Where-Object { $_ -like '*:*' } $ip6CustomObjects = @() if ($endpointSet.category -in ("Optimize")) { $ip6CustomObjects = $ip6s | ForEach-Object { [PSCustomObject]@{ category = $endpointSet.category; ip = $_; tcpPorts = $endpointSet.tcpPorts; udpPorts = $endpointSet.udpPorts; } } } $ip6CustomObjects } # write output to screen # Write-Output ("Client Request ID: " + $clientRequestId) # Write-Output ("Last Version: " + $lastVersion) # Write-Output ("New Version: " + $version.latest) # Write-Output "" # Write-Output "IPv4 Firewall IP Address Ranges" ($flatIp4s.ip | Sort-Object -Unique) -join "," | Out-String # Write-Output "IPv6 Firewall IP Address Ranges" # ($flatIp6s.ip | Sort-Object -Unique) -join "," | Out-String # Write-Output "URLs for Proxy Server" # ($flatUrls.url | Sort-Object -Unique) -join "," | Out-String # Write-Output ("IP and URL data written to " + $datapath) # write output to data file # Write-Output "Office 365 IP and UL Web Service data" | Out-File $datapath # Write-Output "Worldwide instance" | Out-File $datapath -Append # Write-Output "" | Out-File $datapath -Append # Write-Output ("Version: " + $version.latest) | Out-File $datapath -Append # Write-Output "" | Out-File $datapath -Append # Write-Output "IPv4 Firewall IP Address Ranges" | Out-File $datapath -Append # ($flatIp4s.ip | Sort-Object -Unique) -join "," | Out-File $datapath -Append # Write-Output "" | Out-File $datapath -Append # Write-Output "IPv6 Firewall IP Address Ranges" | Out-File $datapath -Append # ($flatIp6s.ip | Sort-Object -Unique) -join "," | Out-File $datapath -Append # Write-Output "" | Out-File $datapath -Append # Write-Output "URLs for Proxy Server" | Out-File $datapath -Append # ($flatUrls.url | Sort-Object -Unique) -join "," | Out-File $datapath -Append } else { Write-Host "Office 365 worldwide commercial service instance endpoints are up-to-date." } - id: powershell_install1 name: Install PowerShell type: powershell_install depends: [] properties: version: latest - id: ips name: Create Array type: set_variables depends: - GetO365 properties: vars: - key: ips value: '{|steps.GetO365.result.stdout | split('','')|}' - id: p1 name: O365 Builder + Upload type: pipeline depends: - ips properties: pipeline_id: 65095199c75cbe2045b641a5 pipeline_revision_id: latest variables: ips: '{|loop.item|}' index: '{|loop.index|}' firewallcreds: '{|vars.firewallcreds|}' firewallhostname: '{|vars.firewallhostname|}' subnetarray: - 0.0.0.0 - 128.0.0.0 - 192.0.0.0 - 224.0.0.0 - 240.0.0.0 - 248.0.0.0 - 252.0.0.0 - 254.0.0.0 - 255.0.0.0 - 255.128.0.0 - 255.192.0.0 - 255.224.0.0 - 255.240.0.0 - 255.248.0.0 - 255.252.0.0 - 255.254.0.0 - 255.255.0.0 - 255.255.128.0 - 255.255.192.0 - 255.255.224.0 - 255.255.240.0 - 255.255.248.0 - 255.255.252.0 - 255.255.254.0 - 255.255.255.0 - 255.255.255.128 - 255.255.255.192 - 255.255.255.224 - 255.255.255.240 - 255.255.255.248 - 255.255.255.252 - 255.255.255.254 - 255.255.255.255 each: '{|vars.ips|}' outputs: - key: ips value: '{|1|}' layout: elements: - id: GetO365 position: x: -155 'y': -75 links: - sourceId: powershell_install1 sourcePort: bottom targetPort: top vertices: [] - id: powershell_install1 position: x: -155 'y': -160 links: [] - id: ips position: x: -155 'y': 10 links: - sourceId: GetO365 sourcePort: bottom targetPort: top vertices: [] - id: p1 position: x: -155 'y': 100 links: - sourceId: ips sourcePort: bottom targetPort: top vertices: []
O365 Builder Pipeline:
--- variables: - type: String name: ips key: ips required: false visible: true default: false - type: StringArray name: subnetarray key: subnetarray value: - 0.0.0.0 - 128.0.0.0 - 192.0.0.0 - 224.0.0.0 - 240.0.0.0 - 248.0.0.0 - 252.0.0.0 - 254.0.0.0 - 255.0.0.0 - 255.128.0.0 - 255.192.0.0 - 255.224.0.0 - 255.240.0.0 - 255.248.0.0 - 255.252.0.0 - 255.254.0.0 - 255.255.0.0 - 255.255.128.0 - 255.255.192.0 - 255.255.224.0 - 255.255.240.0 - 255.255.248.0 - 255.255.252.0 - 255.255.254.0 - 255.255.255.0 - 255.255.255.128 - 255.255.255.192 - 255.255.255.224 - 255.255.255.240 - 255.255.255.248 - 255.255.255.252 - 255.255.255.254 - 255.255.255.255 required: false visible: false default: true - type: Number name: index key: index required: false visible: true default: false - type: Credential name: firewallcreds key: firewallcreds required: true visible: true default: false allowed_types: - username_password - type: String name: firewallhostname key: firewallhostname required: false visible: true default: false steps: - id: Split name: split type: set_variables depends: [] properties: vars: - key: ip value: '{|vars.ips | split(''/'') | first|}' - key: tmp value: '{|vars.ips | split(''/'') | last|}' - key: number value: '{|vars.index |}' - key: subnet value: '{|vars.subnetarray[vars.tmp]|}' - id: p1 name: Set IP Host type: pipeline depends: - Split properties: pipeline_id: 627ec8fd2038a018ef03de65 pipeline_revision_id: 650a02feeccbfcd0be7f213b variables: credential: '{|vars.firewallcreds|}' hostname: '{|vars.firewallhostname|}' port: 4444 name: '{|"M365_" + vars.number|}' ipFamily: IPv4 hostType: Network ipAddress: '{|vars.ip|}' subnet: '{|vars.subnet|}' groups: - M365 outputs: - key: ip value: '{|vars.ip|}' - key: subnet value: '{|vars.subnet|}' - key: number value: '{|vars.number|}' layout: elements: - id: Split position: x: -195 'y': -60 links: [] - id: p1 position: x: -195 'y': 20 links: - sourceId: Split sourcePort: bottom targetPort: top vertices: []
Create the Pipeline and use the Editor to copy/paste the content in your Pipeline.
Use the same names for your Pipelines like i did.
What you have to do: You have to enter your firewall IP and your Firewall Creds to Factory in the "Get-O365Webservices" Pipeline.
You can also generate a Job, which runs automatically each Day with different Creds/Hostnames, if you want.
Talking about the Pipelines in Detail:
Factory installs Powershell, uses the Microsoft Powershell Script to fetch the IP Addresses. Converts the output to an Array and Builds per IP an own API Call for the Firewall.
Change title.
[bearbeitet von: LuCar Toni um 9:49 AM (GMT -7) am 22 Mar 2024]