(Scheduled) M365 Endpoints includes automation #381
This file contains 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
name: (Scheduled) M365 Endpoints includes automation | |
permissions: | |
contents: write | |
pull-requests: write | |
actions: read | |
on: | |
schedule: | |
- cron: "0 */6 * * *" | |
workflow_dispatch: | |
jobs: | |
get-previous-run: | |
if: github.repository_visibility == 'private' | |
name: Get previous run ID | |
runs-on: ubuntu-latest | |
continue-on-error: true | |
outputs: | |
PreviousRun: ${{ steps.previous_run.outputs.previous_run_id }} | |
steps: | |
- name: Get previous run ID | |
id: previous_run | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const response = await github.rest.actions.listWorkflowRuns({ | |
owner: "MicrosoftDocs", | |
repo: "microsoft-365-docs-pr", | |
workflow_id: 117238499, | |
branch: "main", | |
status: "success", | |
per_page: 2, | |
}); | |
// Find the previous run by looking at the second run in the list | |
const previousRun = response.data.workflow_runs.find(run => run.id !== context.runId); | |
core.setOutput("previous_run_id", previousRun.id); | |
- name: Output previous run ID | |
run: | | |
echo "Previous run ID: ${{ steps.previous_run.outputs.previous_run_id }}" | |
update-includes: | |
if: github.repository_visibility == 'private' | |
needs: get-previous-run | |
name: Update includes | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/download-artifact@v4 | |
name: Download previous run data artifact | |
continue-on-error: true | |
with: | |
name: RunData | |
path: "${{ github.workspace }}/run-data/" | |
run-id: ${{ needs.get-previous-run.outputs.PreviousRun}} | |
github-token: ${{ github.token }} | |
- name: Process endpoint data | |
shell: pwsh | |
env: | |
AccessToken: ${{ secrets.MARVEL_REPO_ACCESS_TOKEN }} | |
TargetOrganization: "MicrosoftDocs" | |
TargetRepository: "microsoft-365-docs-pr" | |
TargetPath: "microsoft-365/includes" | |
EndpointsContentOwner: "kelleyvice-msft" | |
run: | | |
# GetServiceInstanceEndpoints | |
# Original script created 4-December-2018 by Peter Worley v-peworl@microsoft.com | |
# Script updated for Azure Torus 2-September-2020 by David Strome dstrome@microsoft.com | |
# Script updated for GitHub workflows 10-September-2024 by David Strome dstrome@microsoft.com | |
<# | |
This script retreives Office 365 endpoint sets for each service instance | |
from the endpoints web service, converts the endpoint sets into tables, | |
saves the tables in a markdown file for each service instance, | |
then uploads each markdown file to a GitHub repository for automated | |
publishing. | |
#> | |
# Set default encoding for file output to ASCII | |
$PSDefaultParameterValues['*:Encoding'] = 'ASCII' | |
function Set-ScriptOptions { | |
<# | |
.SYNOPSIS | |
Set up script variables | |
.DESCRIPTION | |
Define variables, directories, service instances, and service areas, | |
as well as GitHub user, authentication, & repository information. | |
Environment specific changes to the script can be done here. | |
#> | |
# Web Service Address Setup | |
# Web Service root URL | |
$script:ws = "https://endpoints.office.com" | |
Write-Host "Setting script options" | |
# Script Environment Setup | |
# Paths & files for storing client ID, service instance versions, and script output files | |
$script:dataPath = "$env:GITHUB_WORKSPACE/run-data/" # path to store data files | |
$clientIDFile = $dataPath + "endpoints-clientid.txt" # file to store client request id | |
$script:siVersionFile = $dataPath + "endpoints-service-instance-versions.json" # file to store service instance versions | |
write-host "Storage path: $dataPath" | |
# Output File Setup | |
# HTML commented-out automation notice to display at top of output files | |
$script:scriptContact = "the Office 365 Endpoints team" # who should be contacted for script questions/changes/etc. | |
$script:fileNotice = "<!--THIS FILE IS AUTOMATICALLY GENERATED. MANUAL CHANGES WILL BE OVERWRITTEN.-->`n<!--Please contact " + $scriptContact + " with any questions.-->" | |
# Naming convention for output file (appended after the service instance name) | |
$script:outFileSuffix = " Endpoints.md" | |
# Create output directory | |
If (!$(Test-Path $dataPath)) { mkdir $dataPath } | |
# Client ID Setup | |
# Fetch the local client request ID if data file exists; otherwise generate a new one | |
if (Test-Path $clientIDFile) { | |
Write-Host "Getting client request ID" | |
$script:clientRequestId = Get-Content -Path $clientIDFile | |
} | |
else { | |
Write-Host "Creating client request ID" | |
$script:clientRequestId = [GUID]::NewGuid().Guid | |
@($clientRequestId) | Out-File -FilePath $clientIDFile | |
} | |
# Service Instance Setup | |
# Create a lookup hash of service instances from the WS and their corresponding display names | |
# This name determines the output file header, as well as the file name | |
# (The file name is the official name + output file suffix, converted to lowercase and replacing spaces with dashes) | |
$script:siHash = @{} | |
$siHash.China = "Office 365 operated by 21Vianet" | |
$siHash.Germany = "Office 365 Germany" | |
$siHash.USGovDoD = "Office 365 U.S. Government DoD" | |
$siHash.USGovGCCHigh = "Office 365 U.S. Government GCC High" | |
$siHash.Worldwide = "Office 365 Worldwide" | |
# Service Area Setup | |
# Create a list of service areas from the WS | |
# This could be automated, but we want to sort manually | |
$script:allServiceAreas = @("Exchange", "SharePoint", "Skype", "Common") | |
# GitHub Setup | |
# Create github HTTP authentication header | |
$script:OfficedocsprToken = $env:AccessToken | |
$script:GitHubHeaders = @{} | |
$script:GitHubHeaders.Add("Authorization","token $($OfficedocsprToken)") | |
$script:GitHubHeaders.Add("User-Agent", "OfficeDocs") | |
# GitHub user and repo values to use | |
$script:Organization = $env:TargetOrganization # The GitHub user that owns the repository | |
$script:Repository = $env:TargetRepository # The remote repository to upload to | |
$script:TargetBranch = "M365-Endpoints" | |
$script:DefaultBranch = Get-RepoDefaultBranch -Headers $script:GitHubHeaders -Org $script:Organization -Repo $script:Repository # The remote branch | |
$script:RepoDir = $env:TargetPath # The target directory in the remote repository | |
$script:EndpointsContentOwner = $env:EndpointsContentOwner | |
$script:PrDescription = "@MicrosoftDocs/marveldocs-pubops`n`nThis is an automated pull request to update M365 Endpoints include files in **$RepoDir**. Before merging this PR, confirm the following:`n`n- There are no more than **$($siHash.count)** files in the PR`n- The only files in the pull request end with **$($OutFileSuffix.Trim())**`n- There are no obvious formatting issues with the content`n- The pull request is from **$TargetBranch** to **$DefaultBranch**`n`nIf issues are found with the PR, remove the **Sign off** label and add the **Back to submitter** label, assign the PR to **$EndpointsContentOwner**, and @-mention **$EndpointsContentOwner** with instructions on how to resolve the issue.`n`nAcrolinx issues can be ignored per exception: https://office.visualstudio.com/MAX/_workitems/edit/9352479`n`nIf no issues are found, merge the PR. If you have questions, email marveldocs-admins." | |
} # end Set-ScriptOptions function | |
function Get-RepoDefaultBranch { | |
param( | |
$Headers, | |
$Org, | |
$Repo | |
) | |
<# | |
.SYNOPSIS | |
Retrieve the default branch for the repo from GitHub | |
.DESCRIPTION | |
This function retrieves the repo's default branch dynamically in the event that | |
it's modified. A call retrieves the repo config data and the default_branch property | |
retrieved from that data. | |
#> | |
$DefaultBranch = $Null | |
$script:SetDefaultBranchSuccess = $False | |
# Get the default branch for Github | |
Try { | |
Write-Host "Getting default branch for $Repo. URL: https://api.github.com/repos/$Org/$Repo" | |
$DefaultBranch = (Invoke-RestMethod -Headers $Headers -Uri https://api.github.com/repos/$Org/$Repo -ErrorAction Stop -ErrorVariable GitHubDefaultBranchError).default_branch | |
# $DefaultBranch = "dstrome-test" | |
Write-Host "Default for $Repo is `"$DefaultBranch`"" | |
$script:SetDefaultBranchSuccess = $True | |
} Catch { | |
Write-Host "***Failure retrieving default branch for $Repository. <p>Error: $($GitHubDefaultBranchError.ErrorRecord)" | |
} | |
Return $DefaultBranch | |
} # end Get-RepoDefaultBranch function | |
function Get-UpdateList { | |
<# | |
.SYNOPSIS | |
Create a list of service instances that need to be updated | |
.DESCRIPTION | |
This function calls the web service to get the current versions | |
for each service instance, checks the versions against a local file | |
containing the versions from the last time the script was run, then | |
builds a list of service instances that need updates. | |
.PARAMETER siList | |
A list of service instances | |
.PARAMETER versionFile | |
A path to a file which stores service instance versions | |
#> | |
param ($siList, $versionFile) | |
# Fetch the previous service instance versions from the local | |
# version file if it exists, otherwise create a new version file | |
if (Test-Path $versionFile) { | |
$previousVersions = Get-Content -Path $versionFile | ConvertFrom-Json | |
} | |
else { | |
$previousVersions = "" | |
} | |
Write-Host "Create list of service instances that need updates" | |
# Update the local version file with the current service instance versions | |
$siList | ConvertTo-Json | Out-File -FilePath $versionFile | |
# Build a hash with previous service instance versions for comparison | |
$siPreviousVersionHash = @{} | |
foreach ($item in $previousVersions) { | |
$instance = $item.instance; | |
$latest = $item.latest; | |
$siPreviousVersionHash.$instance = $latest; | |
} | |
# Compare the previous service instance versions with the current versions; | |
# Build a list of service instances that need updates | |
$newSiList = @() | |
foreach ($item in $siList) { | |
$instance = $item.instance; | |
$latest = $item.latest; | |
if ($siPreviousVersionHash.$instance -lt $latest) { | |
$newSiList += $instance | |
} | |
} | |
return $newSiList | |
} # end Get-UpdateList function | |
function Get-RemoteDirectoryListing { | |
<# | |
.SYNOPSIS | |
Create the directory listing of a remote repository | |
.DESCRIPTION | |
This function configures TLS, connects to GitHub, then gets the SHA | |
values of the files in the remote repository so they can be updated. | |
#> | |
# Fix TLS version so we can connect to GitHub | |
[net.servicepointmanager]::securityprotocol = [net.securityprotocoltype]::Tls12 | |
Write-Host "Get file list from GitHub" | |
# Try to get $TargetBranch. If it exists, get the file list from $TargetBranch. If not, get it from $DefaultBranch. | |
Try { | |
Invoke-RestMethod -uri "https://api.github.com/repos/$Organization/$Repository/git/ref/heads/$TargetBranch" -headers $GitHubHeaders -Method Get | Out-Null | |
Write-Host "Branch $TargetBranch exists. Getting file list from $TargetBranch." | |
# We need the SHA of the existing files on GitHub in order to update (i.e. replace) them. | |
# To do that we get the repository directory listing. | |
$repoList = Invoke-RestMethod -Headers $GitHubHeaders -Uri https://api.github.com/repos/$Organization/$Repository/contents/$($RepoDir)?ref=$($TargetBranch) | |
} Catch { | |
Write-Host "Branch $TargetBranch does not exist. Getting file list from $DefaultBranch." | |
# We need the SHA of the existing files on GitHub in order to update (i.e. replace) them. | |
# To do that we get the repository directory listing. | |
$repoList = Invoke-RestMethod -Headers $GitHubHeaders -Uri https://api.github.com/repos/$Organization/$Repository/contents/$($RepoDir)?ref=$($DefaultBranch) | |
} | |
# Create a hash table with the name and SHA of each file in the remote repository | |
# This will be used when uploading the output files | |
$shaHash = @{} | |
foreach ($item in $repoList) { | |
$sname = $item.name | |
$ssha = $item.sha | |
$shaHash.$sname = $ssha | |
} | |
return $shaHash | |
} # end Get-RemoteDirectoryListing function | |
function ConvertTo-Markdown { | |
<# | |
.SYNOPSIS | |
Converts a PowerShell object to a Markdown table. | |
.DESCRIPTION | |
This function converts a PowerShell object (endpoint data) to a markdown table | |
Original function: https://gist.github.com/mac2000/86150ab43cfffc5d0eef | |
.EXAMPLE | |
$data | ConvertTo-Markdown | |
.EXAMPLE | |
ConvertTo-Markdown($data) | |
#> | |
[CmdletBinding()] | |
[OutputType([string])] | |
Param ( | |
[Parameter( | |
Mandatory = $true, | |
Position = 0, | |
ValueFromPipeline = $true | |
)] | |
[PSObject[]]$collection | |
) | |
Begin { | |
$items = @() | |
$columns = [ordered]@{} | |
Write-Host "Convert data to markdown" | |
} | |
Process { | |
ForEach($item in $collection) { | |
$items += $item | |
$item.PSObject.Properties | ForEach-Object { | |
if(-not $columns.Contains($_.Name) -or $columns[$_.Name] -lt $_.Value.ToString().Length) { | |
$columns[$_.Name] = $_.Value.ToString().Length | |
} | |
} | |
} | |
} | |
End { | |
ForEach($key in $($columns.Keys)) { | |
$columns[$key] = [Math]::Max($columns[$key], $key.Length) | |
} | |
$header = @() | |
ForEach($key in $columns.Keys) { | |
$header += $key | |
} | |
$header -join ' | ' | |
$separator = @() | |
ForEach($key in $columns.Keys) { | |
$separator += '-' * $columns[$key] | |
} | |
$separator -join ' | ' | |
ForEach($item in $items) { | |
$values = @() | |
ForEach($key in $columns.Keys) { | |
$values += $item.($key) | |
} | |
$values -join ' | ' | |
} | |
} | |
} # end ConvertTo-Markdown function | |
function Format-Ports { | |
<# | |
.SYNOPSIS | |
Sorts and formats a list of ports | |
.DESCRIPTION | |
Splits a string of ports into a list of ports, then separates | |
the individual ports from the port ranges and sorts them | |
.PARAMETER portList | |
A string containing a comma-delimited list of ports | |
#> | |
param ($portList) | |
# The port entry from the web service is a string; we need to convert it into an array | |
$splitPorts = @() | |
$splitPorts = $portList.Split(",") | |
# Separate the individual ports from port ranges and sort them | |
# If it has a "-" then it's a port range, otherwise it's a port | |
$tempPorts = $splitPorts | Where-Object {$_ -notlike '*-*'} | Sort-Object | |
$tempRanges = $splitPorts | Where-Object {$_ -like '*-*'} | Sort-Object | |
# Combine the two lists into a single object so we can specify a delimiter later | |
$ports = @() | |
$ports += $tempPorts | |
$ports += $tempRanges | |
# Return the formatted arrays of ports and ranges | |
return $ports | |
} # end function Format-Ports | |
function Add-OutputFile { | |
<# | |
.SYNOPSIS | |
Creates an output file for a service instance | |
.DESCRIPTION | |
Creates an output file for a service instance at a given path, | |
then places a descriptive header at the top of the file. | |
The output file will be referenced and added to throughout the script. | |
.PARAMETER siList | |
A list of service instances with versions | |
.PARAMETER si | |
A service instance | |
.PARAMETER path | |
A path for the output file | |
#> | |
param($siList, $si, $path) | |
Write-Host "Create output file for $si" | |
# Generate the file name, converted to lowercase and replacing spaces with dashes for compatibility | |
$script:siFile = (($siHash.$si + $outFileSuffix) -replace (" ", "-")).ToLower() | |
Out-File -FilePath ($path + $siFile) -Force # overwrite previous output file if it exists | |
$fileNotice | Out-File -Append -FilePath ($path + $siFile) | |
"<!--" + $si + " endpoints version " + ($siList | Where-Object {$_.instance -eq $si} | ForEach-Object {$_.latest}) + "-->" | Out-File -Append -FilePath ($path + $siFile) | |
"<!--File generated " + (Get-Date -Format yyyy-MM-dd` HH:mm:ss.ffff) + "-->" | Out-File -Append -FilePath ($path + $siFile) | |
# Commenting out H1 header as it's currently not needed in the include file | |
# "`n# " + $siHash.$si + " Endpoints`n" | Out-File -Append -FilePath ($path + $siFile) | |
} # end Add-OutputFile function | |
function Write-ServiceAreaHeader { | |
param($saName, $path, $file) | |
Write-Host "Write service header for $saName" | |
# Add a header with the service area name to the output file | |
"`n## " + $saName + "`n" | Out-File -Append -FilePath ($path + $file) | |
} # end function Write-ServiceAreaHeader | |
function ConvertTo-EndpointObject { | |
<# | |
.SYNOPSIS | |
Builds an endpoint set object for a service area | |
.DESCRIPTION | |
Builds a custom PS object from a list of formatted endpoint sets for the | |
specified service area. The resulting object will have normalized fields | |
corresponding to the desired fields in the final output file. | |
.PARAMETER eps | |
A list of formatted endpoint sets | |
.PARAMETER sa | |
A service area name | |
.NOTES | |
The PSCustomObject attributes will correspond to the column headers | |
when converted to a markdown table. | |
#> | |
param ($eps, $sa) | |
Write-Host "Create endpoint set for $sa" | |
# Loop through the endpoint sets in the service instance and build | |
# an object out of the endpoint sets in the current service area | |
$eps | ForEach-Object { | |
if ($_.serviceArea -eq $sa) { | |
$endpointSet = $_ | |
# Join the URLs/IPs/ports into strings for output. Define the "-join" separator | |
# (generally "<BR>" or "," or ", ") between each URL, each IP address, and each port. | |
# Use <BR> if you want each IP/URL address/port to appear on a separate line | |
$urls = ($(if ($endpointSet.urls.Count -gt 0) {"``" + (($endpointSet.urls | Where-Object { $_ -ne $null }) -join ", ") + "``"} else { @() })) | |
$ips = ($(if ($endpointSet.ips.Count -gt 0) {"``" + (($endpointSet.ips | Where-Object { $_ -ne $null }) -join ", ") + "``"} else { @() })) | |
$tcpPorts = ($(if ($endpointSet.tcpPorts) {"**TCP:** " + ((Format-Ports $endpointSet.tcpPorts) -join ", ")}) | Where-Object { $_ -ne $null }) -join ", " | |
$udpPorts = ($(if ($endpointSet.udpPorts) {"**UDP:** " + ((Format-Ports $endpointSet.udpPorts) -join ", ")}) | Where-Object { $_ -ne $null }) -join ", " | |
# Set up the category field to also display required or optional, as well as notes | |
if ($endpointSet.required) { | |
$category = $endpointSet.category + "<BR>Required" | |
} | |
else { | |
$category = $endpointSet.category + "<BR>Optional<BR>" + "**Notes:** " + $endpointSet.notes | |
} | |
# Create a PowerShell object from the normalized endpoint data | |
# This object will be converted into a markdown table | |
# The attributes of this object determine the columns in the table | |
# The order of the object attributes determine the order of the table columns | |
[PSCustomObject] @{ | |
ID = $endpointSet.id; | |
Category = $category; | |
ER = $(if($endpointSet.expressRoute) {"Yes"} else {"No"}); # Use "Yes/No" instead of "True/False" | |
Addresses = ($(if($urls){$urls}), $(if($ips){$ips}) | Where-Object { $_ -ne $null }) -join "<BR>"; | |
Ports = ($(if($tcpPorts){$tcpPorts}), $(if($udpPorts){$udpPorts}) | Where-Object { $_ -ne $null }) -join "<BR>"; | |
} | |
} # end if (test for service area) | |
} # end of $endpointSet object loop | |
} # end function ConvertTo-EndpointObject | |
function Update-TenantURLs { | |
# This function exists so we can put "<tenant>" in tenant wildcard URLs | |
param ($epsList, $placeholderString, $replaceString) | |
Write-Host "Update tenant URLs" | |
$epsList | Where-Object {$_.urls -like "*$placeHolderString*"} | ForEach-Object { | |
$_.urls = ($_.urls -replace $placeholderString, $replaceString) | |
} | |
return $epsList | |
} # end function Update-TenantURLs | |
function Send-ToGitHub { | |
<# | |
.SYNOPSIS | |
Uploads a file to a GitHub repository using the GitHub API | |
.DESCRIPTION | |
The function uses the GitHub variables declared earlier in the script | |
(Organization, Repository, RepoDir, Branch, GitHubHeaders) to upload a file | |
to a repository. | |
.PARAMETER path | |
A path where the directory where theupload file can be found | |
.PARAMETER file | |
The name of the file to be uploaded | |
#> | |
param($path, $file) | |
# Get the sha of the file from the repository list, | |
# based on the matching local file name (previously created) | |
Write-Host "Upload $file to GitHub" | |
$sha = $shaHash.$file | |
# Convert the local file to be uploaded to Base64 (as required by GitHub) | |
$Base64Content = [System.Convert]::ToBase64String([IO.File]::ReadAllBytes($path + $file)) | |
# Build the body of the message with the file info. | |
$Body = @{ | |
message = "Uploaded file: " + ($file) + " - " + (Get-Date -Format yyyy-MM-dd` HH:mm:ss.ffff) | |
content = $Base64Content | |
sha = $sha | |
branch = $TargetBranch | |
} | ConvertTo-Json | |
# Check if $TargetBranch exists and, if not, create it from $DefaultBranch | |
Try { | |
Invoke-RestMethod -uri "https://api.github.com/repos/$Organization/$Repository/git/ref/heads/$TargetBranch" -headers $GitHubHeaders -Method Get | Out-Null | |
} Catch { | |
Write-Host "Branch $TargetBranch does not exist. Creating branch $TargetBranch from $DefaultBranch." | |
$DefaultBranchData = Invoke-RestMethod -uri "https://api.github.com/repos/$Organization/$Repository/git/ref/heads/$DefaultBranch" -Headers $GitHubHeaders -Method Get | |
$BranchBody = @{ | |
ref = "refs/heads/$TargetBranch" | |
sha = $DefaultBranchData.object.sha | |
} | ConvertTo-Json | |
Invoke-RestMethod -uri "https://api.github.com/repos/$Organization/$Repository/git/refs" -headers $GitHubHeaders -Method POST -Body $BranchBody | |
} | |
Write-Host $Body | |
Write-Host "https://api.github.com/repos/$Organization/$Repository/contents/$($RepoDir)/$($File)?ref=$TargetBranch" | |
# Upload the file | |
Invoke-RestMethod -Headers $GitHubHeaders -Uri https://api.github.com/repos/$Organization/$Repository/contents/$($RepoDir)/$($File)?ref=$TargetBranch -Method PUT -Body $Body | |
} # end Send-ToGitHub function | |
Function New-PullRequest { | |
$BranchResponse = $Null | |
Write-Host "Checking if $TargetBranch exists" | |
Try { | |
$BranchResponse = Invoke-RestMethod -uri "https://api.github.com/repos/$Organization/$Repository/git/ref/heads/$TargetBranch" -headers $GitHubHeaders -Method Get -ErrorAction Stop | |
} Catch { | |
Write-Host "Branch doesn't exist." | |
} | |
If ($BranchResponse) { | |
Write-Host "Checking if $TargetBranch is ahead of $DefaultBranch" | |
Try { | |
$CompareResponse = Invoke-RestMethod -Uri "https://api.github.com/repos/$Organization/$Repository/compare/$DefaultBranch...$TargetBranch" -Method GET -Headers $GitHubHeaders | |
} Catch { | |
Throw "Failed to check if $TargetBranch is ahead of $DefaultBranch. Error: $($Error[0])" | |
} | |
If ($CompareResponse.ahead_by -gt 0) { | |
Write-Host "$TargetBranch has changes ahead of $DefaultBranch" | |
Write-Host "Checking if a pull request exists between $TargetBranch and $DefaultBranch" | |
Try { | |
$PrsResponse = Invoke-RestMethod -Uri "https://api.github.com/repos/$Organization/$Repository/pulls?head=$($Organization):$TargetBranch&base=$DefaultBranch&state=open" -Method GET -Headers $GitHubHeaders | |
} Catch { | |
Throw "Failed to check if pull request exists between $TargetBranch and $DefaultBranch. Error: $($Error[0])" | |
} | |
If (($PrsResponse | Measure-Object).Count -le 0) { | |
Write-Host "No existing pull request. Creating a new PR from $TargetBranch to $DefaultBranch" | |
$PrBody = @{ | |
title = "Merge $TargetBranch into $DefaultBranch" | |
head = $TargetBranch | |
base = "$DefaultBranch" | |
body = "$PrDescription" | |
} | ConvertTo-Json | |
Try { | |
$PrResponse = Invoke-RestMethod -Uri "https://api.github.com/repos/$Organization/$Repository/pulls" -Method POST -Headers $GitHubHeaders -Body $PrBody | |
Write-Host "Created pull request $($PrResponse.html_url)" | |
$LabelBody = @{ | |
labels = @("Sign off") | |
} | ConvertTo-Json | |
Invoke-RestMethod -Uri "https://api.github.com/repos/$Organization/$Repository/issues/$($PrResponse.number)/labels" -Method POST -Headers $GitHubHeaders -Body $LabelBody | |
} Catch { | |
Throw "Failed to create pull request. Error: $($Error[0])" | |
} | |
} Else { | |
Write-Host "Pull request exists. " | |
} | |
} | |
} | |
} | |
### END FUNCTION BLOCK ### | |
### BEGIN EXECUTION BLOCK ### | |
Write-Host "Starting endpoints update script" | |
# Set up the script variables | |
Set-ScriptOptions | |
If ($SetDefaultBranchSuccess) { | |
# Get the current list of service instances and versions from the web service | |
$allServiceInstances = Invoke-RestMethod -Uri ($ws + "/version?clientrequestid=" + $clientRequestId) | |
# Check the service instances for new versions, then if necessary | |
# create a list of service instances needing updates | |
$serviceInstances = Get-UpdateList $allServiceInstances $siVersionFile | |
# If any service instances need to be updated, we need the SHA of the | |
# existing files on GitHub in order to replace them with updated files | |
if ($serviceInstances.Count -gt 0) { | |
$shaHash = Get-RemoteDirectoryListing $serviceInstances | |
} Else { | |
Write-Host "No service instances need updates" | |
} | |
# Loop through the list of service instances to get updated endpoint sets, format | |
# the endpoint data, convert to markdown, save an output file, and upload to GitHub. | |
$serviceInstances | ForEach-Object { | |
$currentServiceInstance = $_ | |
Write-Host "Processing $currentServiceInstance" | |
# Create the initial output file for the service instance ($siFile). We will | |
# write to this file as we build the markdown table for each service area. | |
Add-OutputFile $allServiceInstances $currentServiceInstance $dataPath | |
# DEALING WITH TENANT WILDCARD URLS | |
# We need to display <tenant> in tenant wildcard URLs, but we can't | |
# actually pass that value to the web service anymore. | |
# The following workaround uses placeholder text for tenant URLs, | |
# then replaces the placeholder text with the text we need displayed. | |
$tenantPlaceHolderText = "AnyUniqueString" | |
$tenantTextToDisplay = "<tenant>" | |
$endpointSets = "" | |
Write-Host "Retrieving endpoint set for $currentServiceInstance from M365 web service" | |
# Call the web service to get the endpoint sets for the current service instance | |
$endpointSets = Invoke-RestMethod -Uri ($ws + "/endpoints/"+ $currentServiceInstance + "?tenantname=" + $tenantPlaceHolderText + "&clientRequestId=" + $clientRequestId) | |
# Only continue if we have valid endpoint set data | |
if ($endpointSets -ne "") { | |
Write-Host "Retrieved endpoint sets" | |
# Replace the tenant placeholder text with the text we need to display | |
$endpointSets = Update-TenantURLs $endpointSets $tenantPlaceHolderText $tenantTextToDisplay | |
# Group the endpoint sets by service area and convert to markdown, then append to the output file | |
$allServiceAreas | ForEach-Object { | |
$currentServiceArea = $_ | |
Write-Host "Processing $currentServiceArea" | |
# Create a custom object from the list of normalized endpoint sets | |
$saEndpointSetsObject = ConvertTo-EndpointObject $endpointSets $currentServiceArea | |
# Get the official display name for the service area | |
$saDisplayName = $endpointSets | Where-Object -Property serviceArea -EQ $currentServiceArea | ForEach-Object {$_.serviceAreaDisplayName} | Select-Object -Unique | |
# Put a header at the top of each service area section in the markdown | |
Write-ServiceAreaHeader $saDisplayName $dataPath $siFile | |
# Convert the endpoint sets object to markdown and append to the output file for the current service instance | |
($saEndpointSetsObject) | Select-Object * | ConvertTo-Markdown | Out-File -Append -FilePath ($dataPath + $siFile) | |
} # end allServiceAreas loop | |
# Upload the final output file to GitHub | |
Send-ToGitHub $dataPath $siFile | |
} Else { | |
Write-Host "Didn't receive any endpoint sets from webservice for $currentServiceInstance" | |
} | |
} # end serviceInstances loop | |
# Create a new pull request. New-PullRequest will only create a new PR if one is needed. | |
New-PullRequest | |
} # set default branch check | |
ls -alr $dataPath | |
Write-Host "Finished endpoints update script" | |
- uses: actions/upload-artifact@v4 | |
name: Upload run data artifact | |
with: | |
name: RunData | |
path: "${{ github.workspace }}/run-data/endpoints*" |