1+ [CmdletBinding ()]
2+ param (
3+ [Parameter (Mandatory = $true )]
4+ [string ]
5+ $AppClientId ,
6+ [Parameter (Mandatory = $true )]
7+ [string ]
8+ $AppPrivateKeyContents ,
9+ [Parameter (Mandatory = $true )]
10+ [ValidatePattern (' ^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$' , ErrorMessage = " Repository must be in the format 'owner/repo' (e.g. 'octocat/hello-world')" )]
11+ [string ]
12+ $Repository
13+ )
14+
15+ $ErrorActionPreference = " Stop"
16+
17+ function Generate-AppToken {
18+ param (
19+ [string ]
20+ $ClientId ,
21+ [string ]
22+ $PrivateKeyContents
23+ )
24+
25+ $header = [Convert ]::ToBase64String([System.Text.Encoding ]::UTF8.GetBytes((ConvertTo-Json - InputObject @ {
26+ alg = " RS256"
27+ typ = " JWT"
28+ }))).TrimEnd(' =' ).Replace(' +' , ' -' ).Replace(' /' , ' _' );
29+
30+ $payload = [Convert ]::ToBase64String([System.Text.Encoding ]::UTF8.GetBytes((ConvertTo-Json - InputObject @ {
31+ iat = [System.DateTimeOffset ]::UtcNow.AddSeconds(-10 ).ToUnixTimeSeconds()
32+ exp = [System.DateTimeOffset ]::UtcNow.AddMinutes(1 ).ToUnixTimeSeconds()
33+ iss = $ClientId
34+ }))).TrimEnd(' =' ).Replace(' +' , ' -' ).Replace(' /' , ' _' );
35+
36+ $rsa = [System.Security.Cryptography.RSA ]::Create()
37+ $rsa.ImportFromPem ($PrivateKeyContents )
38+
39+ $signature = [Convert ]::ToBase64String($rsa.SignData ([System.Text.Encoding ]::UTF8.GetBytes(" $header .$payload " ), [System.Security.Cryptography.HashAlgorithmName ]::SHA256, [System.Security.Cryptography.RSASignaturePadding ]::Pkcs1)).TrimEnd(' =' ).Replace(' +' , ' -' ).Replace(' /' , ' _' )
40+ $jwt = " $header .$payload .$signature "
41+
42+ return $jwt
43+ }
44+
45+ function Generate-InstallationToken {
46+ param (
47+ [string ]
48+ $AppToken ,
49+ [string ]
50+ $InstallationId ,
51+ [string ]
52+ $Repository
53+ )
54+
55+ $uri = " https://api.github.com/app/installations/$InstallationId /access_tokens"
56+ $headers = @ {
57+ Authorization = " Bearer $AppToken "
58+ Accept = " application/vnd.github+json"
59+ " X-GitHub-Api-Version" = " 2022-11-28"
60+ }
61+
62+ $body = @ {
63+ repositories = @ ($Repository )
64+ }
65+
66+ $response = Invoke-RestMethod - Uri $uri - Method Post - Headers $headers - Body (ConvertTo-Json - InputObject $body - Compress - Depth 10 )
67+
68+ return $response.token
69+ }
70+
71+
72+ function Get-OrganizationInstallationId {
73+ param (
74+ [string ]
75+ $AppToken ,
76+ [string ]
77+ $Organization
78+ )
79+
80+ $uri = " https://api.github.com/orgs/$Organization /installation"
81+ $headers = @ {
82+ Authorization = " Bearer $AppToken "
83+ Accept = " application/vnd.github+json"
84+ " X-GitHub-Api-Version" = " 2022-11-28"
85+ }
86+
87+ try {
88+ $response = Invoke-RestMethod - Uri $uri - Method Get - Headers $headers
89+
90+ return $response.id
91+ }
92+ catch [Microsoft.PowerShell.Commands.HttpResponseException ] {
93+ if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode ]::UnprocessableContent -or $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode ]::NotFound) {
94+ return $null
95+ }
96+
97+ throw
98+ }
99+ }
100+
101+ function Get-RepositoryInstallationId {
102+ param (
103+ [string ]
104+ $AppToken ,
105+ [string ]
106+ $Repository
107+ )
108+
109+ $uri = " https://api.github.com/repos/$Repository /installation"
110+ $headers = @ {
111+ Authorization = " Bearer $AppToken "
112+ Accept = " application/vnd.github+json"
113+ " X-GitHub-Api-Version" = " 2022-11-28"
114+ }
115+
116+ try {
117+ $response = Invoke-RestMethod - Uri $uri - Method Get - Headers $headers
118+
119+ return $response.id
120+ }
121+ catch [Microsoft.PowerShell.Commands.HttpResponseException ] {
122+ if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode ]::UnprocessableContent -or $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode ]::NotFound) {
123+ return $null
124+ }
125+
126+ throw
127+ }
128+ }
129+
130+ function Get-UserInstallationId {
131+ param (
132+ [string ]
133+ $AppToken ,
134+ [string ]
135+ $Username
136+ )
137+
138+ $uri = " https://api.github.com/users/$Username /installation"
139+ $headers = @ {
140+ Authorization = " Bearer $AppToken "
141+ Accept = " application/vnd.github+json"
142+ " X-GitHub-Api-Version" = " 2022-11-28"
143+ }
144+
145+ try {
146+ $response = Invoke-RestMethod - Uri $uri - Method Get - Headers $headers
147+
148+ return $response.id
149+ }
150+ catch [Microsoft.PowerShell.Commands.HttpResponseException ] {
151+ if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode ]::UnprocessableContent -or $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode ]::NotFound) {
152+ return $null
153+ }
154+
155+ throw
156+ }
157+ }
158+
159+ function Get-InstallationId {
160+ param (
161+ [string ]
162+ $AppToken ,
163+ [string ]
164+ $Owner ,
165+ [string ]
166+ $Repo
167+ )
168+
169+ $orgInstallationId = Get-OrganizationInstallationId - AppToken $AppToken - Organization $Owner
170+
171+ if ($null -eq $orgInstallationId ) {
172+ $repoInstallationId = Get-RepositoryInstallationId - AppToken $AppToken - Repository " $Owner /$Repo "
173+ }
174+ else {
175+ return $orgInstallationId
176+ }
177+
178+ if ($null -eq $repoInstallationId ) {
179+ $userInstallationId = Get-UserInstallationId - AppToken $AppToken - Username $Owner
180+ }
181+ else {
182+ return $repoInstallationId
183+ }
184+
185+ if ($null -eq $userInstallationId ) {
186+ throw " Installation not found for repository '$Repo '"
187+ }
188+ else {
189+ return $userInstallationId
190+ }
191+ }
192+
193+ $owner , $repo = $Repository -split ' /'
194+
195+ $AppToken = Generate- AppToken - ClientId $AppClientId - PrivateKeyContents $AppPrivateKeyContents
196+
197+ $InstallationId = Get-InstallationId - AppToken $AppToken - Owner $owner - Repo $repo
198+
199+ $InstallationToken = Generate- InstallationToken - AppToken $AppToken - InstallationId $InstallationId - Repository $repo
200+
201+ Write-Output $InstallationToken
0 commit comments