Skip to content

Commit fd27dc1

Browse files
Update publish-AzWebApp to support pull based deployments. (Azure#27228)
Co-authored-by: Yash <[email protected]>
1 parent 428a008 commit fd27dc1

File tree

9 files changed

+3387
-1606
lines changed

9 files changed

+3387
-1606
lines changed

src/Websites/Websites.Test/ScenarioTests/WebAppTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ public void TestPublishWebAppOneDeploy()
110110
TestRunner.RunTestScript("Test-PublishAzureWebAppOneDeploy");
111111
}
112112

113+
[Fact]
114+
[Trait(Category.RunType, Category.LiveOnly)]
115+
public void TestPublishWebAppOneDeployPullWithMSI()
116+
{
117+
TestRunner.RunTestScript("Test-PublishAzureWebAppPullWithMSI");
118+
}
119+
113120
[Fact]
114121
[Trait(Category.AcceptanceType, Category.CheckIn)]
115122
public void TestCloneNewWebApp()

src/Websites/Websites.Test/ScenarioTests/WebAppTests.ps1

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,47 @@ function Test-PublishAzureWebAppOnedeploy
12851285
}
12861286
}
12871287

1288+
# New tests for PublishAzureWebApp to test pull based deployment with MSI
1289+
function Test-PublishAzureWebAppPullWithMSI
1290+
{
1291+
# Setup
1292+
$rgname = Get-ResourceGroupName
1293+
$appName = Get-WebsiteName
1294+
$location = Get-WebLocation
1295+
$planName = Get-WebHostPlanName
1296+
$tier = "Shared"
1297+
1298+
try
1299+
{
1300+
#Setup
1301+
New-AzureRmResourceGroup -Name $rgname -Location $location
1302+
$serverFarm = New-AzureRmAppServicePlan -ResourceGroupName $rgname -Name $planName -Location $location -Tier $tier
1303+
1304+
# Create new web app
1305+
$webapp = New-AzureRmWebApp -ResourceGroupName $rgname -Name $appName -Location $location -AppServicePlan $planName
1306+
1307+
#Configuring jdk and web container
1308+
# Set Java runtime to 1.8 | Tomcat. In order to deploy war, site should be configured to run with stack = TOMCAT
1309+
# or JBOSSEAP (only availble on Linux). In this test case, it creates Windows app.
1310+
$javaVersion="1.8"
1311+
$javaContainer="TOMCAT"
1312+
$javaContainerVersion="8.5"
1313+
$PropertiesObject = @{javaVersion = $javaVersion;javaContainer = $javaContainer;javaContainerVersion = $javaContainerVersion}
1314+
New-AzResource -PropertyObject $PropertiesObject -ResourceGroupName $rgname -ResourceType Microsoft.Web/sites/config -ResourceName "$appName/web" -ApiVersion 2018-02-01 -Force
1315+
1316+
$warPath = Join-Path $ResourcesPath "HelloJava.war"
1317+
$publishedApp = Publish-AzWebApp -ResourceGroupName:$rgname -Name:$appName -ArchivePath:$warPath -Type:war -Clean:$true -TargetPath:/home/site/wwwroot/webapps/ROOT -Force
1318+
1319+
Assert-NotNull $publishedApp
1320+
}
1321+
finally
1322+
{
1323+
# Cleanup
1324+
Remove-AzureRmResourceGroup -Name $rgname -Force
1325+
}
1326+
1327+
}
1328+
12881329
<#
12891330
.SYNOPSIS
12901331
Tests creating a web app with a simple parameterset.

src/Websites/Websites.Test/SessionRecords/Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests/TestPublishWebAppFromWar.json

Lines changed: 395 additions & 527 deletions
Large diffs are not rendered by default.

src/Websites/Websites.Test/SessionRecords/Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests/TestPublishWebAppFromZip.json

Lines changed: 402 additions & 534 deletions
Large diffs are not rendered by default.

src/Websites/Websites.Test/SessionRecords/Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests/TestPublishWebAppOneDeploy.json

Lines changed: 394 additions & 526 deletions
Large diffs are not rendered by default.

src/Websites/Websites.Test/SessionRecords/Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests/TestPublishWebAppOneDeployPullWithMSI.json

Lines changed: 2030 additions & 0 deletions
Large diffs are not rendered by default.

src/Websites/Websites/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Additional information about change #1
1919
-->
2020
## Upcoming Release
21+
* Add support for pull based deployments from a URL with MSI authentication in `Publish-AzWebApp`
2122

2223
## Version 3.3.1
2324
* Migrated Websites.Helper generation from autorest csharp to autorest powershell.

src/Websites/Websites/Cmdlets/WebApps/PublishAzureWebApp.cs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Microsoft.Azure.Commands.WebApps.Models;
1919
using Microsoft.Azure.Management.WebSites.Models;
2020
using Microsoft.WindowsAzure.Commands.Utilities.Common;
21+
using Newtonsoft.Json;
2122
using System;
2223
using System.IO;
2324
using System.Linq;
@@ -40,10 +41,12 @@ public class PublishAzureWebAppCmdlet : WebAppOptionalSlotBaseCmdlet
4041
// Poll status for a maximum of 35 minutes (2100 seconds / 2 seconds per status check)
4142
private const int NumStatusChecks = 1050;
4243

43-
[Parameter(Mandatory = true, HelpMessage = "The path of the archive file. ZIP, WAR, and JAR are supported.")]
44-
[ValidateNotNullOrEmpty]
44+
[Parameter(Mandatory = false, HelpMessage = "The path of the archive file. ZIP, WAR, and JAR are supported.")]
4545
public string ArchivePath { get; set; }
4646

47+
[Parameter(Mandatory = false, HelpMessage = "URL of the artifact. The webapp will pull the artifact from this URL. Ex: \"http://mysite.com/files/myapp.war")]
48+
public string ArchiveURL { get; set; }
49+
4750
[Parameter(Mandatory = false, HelpMessage = "Used to override the type of artifact being deployed.")]
4851
[ValidateSet("war", "jar", "ear", "zip", "static")]
4952
public string Type { get; set; }
@@ -60,6 +63,9 @@ public class PublishAzureWebAppCmdlet : WebAppOptionalSlotBaseCmdlet
6063
[Parameter(Mandatory = false, HelpMessage = "Absolute path that the artifact should be deployed to.")]
6164
public string TargetPath { get; set; }
6265

66+
[Parameter(Mandatory = false, HelpMessage = "AAD identity used for pull based deployments. 'system' will use the app's system assigned identity. An user assigned identity can be used by providing the client ID. Only available for Windows WebApps. Support for Linux WebApps coming soon.")]
67+
public string PullIdentity { get; set; }
68+
6369
[Parameter(Mandatory = false, HelpMessage = "Disables any language-specific defaults")]
6470
public SwitchParameter IgnoreStack { get; set; }
6571

@@ -81,6 +87,9 @@ public override void ExecuteCmdlet()
8187
base.ExecuteCmdlet();
8288
User user = WebsitesClient.GetPublishingCredentials(ResourceGroupName, Name, Slot);
8389

90+
PSSite app = new PSSite(WebsitesClient.GetWebApp(ResourceGroupName, Name, Slot));
91+
bool isLinuxApp = app.Kind != null && app.Kind.ToLower().Contains("linux");
92+
8493
HttpResponseMessage r;
8594
string deploymentStatusUrl = user.ScmUri + "/api/deployments/latest";
8695

@@ -94,6 +103,27 @@ public override void ExecuteCmdlet()
94103
string fileExtention = Path.GetExtension(ArchivePath);
95104
string[] validTypes = { "war", "jar", "ear", "zip", "static" };
96105

106+
if (string.IsNullOrEmpty(ArchivePath) && string.IsNullOrEmpty(ArchiveURL))
107+
{
108+
var rec = new ErrorRecord(new Exception("Either ArchivePath or ArchiveURL need to be provided."), string.Empty, ErrorCategory.InvalidArgument, null);
109+
WriteError(rec);
110+
return;
111+
}
112+
113+
if (!string.IsNullOrEmpty(ArchiveURL) && string.IsNullOrEmpty(Type))
114+
{
115+
var rec = new ErrorRecord(new Exception("Deployment type is mandatory when deploying from URLs. Use -type"), string.Empty, ErrorCategory.InvalidArgument, null);
116+
WriteError(rec);
117+
return;
118+
}
119+
120+
if (!string.IsNullOrEmpty(PullIdentity) && isLinuxApp)
121+
{
122+
var rec = new ErrorRecord(new Exception("Pull with MSI support is not yet available for Linux webapps"), string.Empty, ErrorCategory.InvalidArgument, null);
123+
WriteError(rec);
124+
return;
125+
}
126+
97127
if (!string.IsNullOrEmpty(Type))
98128
{
99129
paramValues["type"] = Type;
@@ -157,11 +187,8 @@ public override void ExecuteCmdlet()
157187

158188
Action zipDeployAction = () =>
159189
{
160-
if (!Path.IsPathRooted(ArchivePath))
161-
ArchivePath = Path.Combine(this.SessionState.Path.CurrentFileSystemLocation.Path, ArchivePath);
162-
using (var s = File.OpenRead(ArchivePath))
190+
using (HttpClient client = new HttpClient())
163191
{
164-
HttpClient client = new HttpClient();
165192
if (this.IsParameterBound(cmdlet => cmdlet.Timeout))
166193
{
167194
// Considering the deployment of large packages the default time(150 seconds) is not sufficient. So increased the timeout based on user choice.
@@ -170,10 +197,37 @@ public override void ExecuteCmdlet()
170197

171198
var token = WebsitesClient.GetAccessToken(DefaultContext);
172199
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.AccessToken);
200+
client.DefaultRequestHeaders.UserAgent.ParseAdd(AzurePSCmdlet.UserAgent);
173201

174-
HttpContent fileContent = new StreamContent(s);
175-
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
176-
r = client.PostAsync(deployUrl, fileContent).Result;
202+
HttpContent archiveContent = null;
203+
204+
// pull based deployment
205+
if (!string.IsNullOrEmpty(ArchiveURL) && string.IsNullOrEmpty(ArchivePath))
206+
{
207+
archiveContent = new StringContent(JsonConvert.SerializeObject(new { packageUri = ArchiveURL, pullIdentity = PullIdentity }));
208+
archiveContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
209+
r = client.PostAsync(deployUrl, archiveContent).Result;
210+
}
211+
//push based deployment
212+
else if (string.IsNullOrEmpty(ArchiveURL) && !string.IsNullOrEmpty(ArchivePath))
213+
{
214+
if (!Path.IsPathRooted(ArchivePath))
215+
{
216+
ArchivePath = Path.Combine(this.SessionState.Path.CurrentFileSystemLocation.Path, ArchivePath);
217+
}
218+
using (var s = File.OpenRead(ArchivePath))
219+
{
220+
archiveContent = new StreamContent(s);
221+
archiveContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
222+
r = client.PostAsync(deployUrl, archiveContent).Result;
223+
}
224+
}
225+
else
226+
{
227+
var rec = new ErrorRecord(new Exception("Could not find artifact source"), string.Empty, ErrorCategory.InvalidArgument, null);
228+
WriteError(rec);
229+
return;
230+
}
177231

178232
// Checking the response of the post request. If the post request fails with 502 or 503 HTTP status
179233
// then deployments/latest endpoint may give false postive result.
@@ -207,7 +261,6 @@ public override void ExecuteCmdlet()
207261

208262
ConfirmAction(this.Force.IsPresent, $"Contents of {ArchivePath} will be deployed to the web app {Name}.", "The web app has been deployed.", Name, zipDeployAction);
209263

210-
PSSite app = new PSSite(WebsitesClient.GetWebApp(ResourceGroupName, Name, Slot));
211264
WriteObject(app);
212265
}
213266
}

src/Websites/Websites/help/Publish-AzWebApp.md

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ Deploys an Azure Web App from a ZIP, JAR, or WAR file using zipdeploy.
1414

1515
### FromWebApp (Default)
1616
```
17-
Publish-AzWebApp -ArchivePath <String> [-Type <String>] [-Clean] [-Async] [-Restart] [-TargetPath <String>]
18-
[-IgnoreStack] [-Reset] [-Force] [-AsJob] [-Timeout <Double>] [-WebApp] <PSSite>
19-
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm]
20-
[<CommonParameters>]
17+
Publish-AzWebApp [-ArchivePath <String>] [-ArchiveURL <String>] [-Type <String>] [-Clean] [-Async] [-Restart]
18+
[-TargetPath <String>] [-PullIdentity <String>] [-IgnoreStack] [-Reset] [-Force] [-AsJob] [-Timeout <Double>]
19+
[-WebApp] <PSSite> [-DefaultProfile <IAzureContextContainer>] [-ProgressAction <ActionPreference>] [-WhatIf]
20+
[-Confirm] [<CommonParameters>]
2121
```
2222

2323
### FromResourceName
2424
```
25-
Publish-AzWebApp -ArchivePath <String> [-Type <String>] [-Clean] [-Async] [-Restart] [-TargetPath <String>]
26-
[-IgnoreStack] [-Reset] [-Force] [-AsJob] [-Timeout <Double>] [-ResourceGroupName] <String> [-Name] <String>
27-
[[-Slot] <String>] [-DefaultProfile <IAzureContextContainer>] [-WhatIf]
28-
[-Confirm] [<CommonParameters>]
25+
Publish-AzWebApp [-ArchivePath <String>] [-ArchiveURL <String>] [-Type <String>] [-Clean] [-Async] [-Restart]
26+
[-TargetPath <String>] [-PullIdentity <String>] [-IgnoreStack] [-Reset] [-Force] [-AsJob] [-Timeout <Double>]
27+
[-ResourceGroupName] <String> [-Name] <String> [[-Slot] <String>] [-DefaultProfile <IAzureContextContainer>]
28+
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
2929
```
3030

3131
## DESCRIPTION
@@ -87,7 +87,22 @@ Type: System.String
8787
Parameter Sets: (All)
8888
Aliases:
8989

90-
Required: True
90+
Required: False
91+
Position: Named
92+
Default value: None
93+
Accept pipeline input: False
94+
Accept wildcard characters: False
95+
```
96+
97+
### -ArchiveURL
98+
URL of the artifact. The webapp will pull the artifact from this URL. Ex: "http://mysite.com/files/myapp.war
99+
100+
```yaml
101+
Type: System.String
102+
Parameter Sets: (All)
103+
Aliases:
104+
105+
Required: False
91106
Position: Named
92107
Default value: None
93108
Accept pipeline input: False
@@ -199,6 +214,36 @@ Accept pipeline input: True (ByPropertyName)
199214
Accept wildcard characters: False
200215
```
201216
217+
### -ProgressAction
218+
{{ Fill ProgressAction Description }}
219+
220+
```yaml
221+
Type: System.Management.Automation.ActionPreference
222+
Parameter Sets: (All)
223+
Aliases: proga
224+
225+
Required: False
226+
Position: Named
227+
Default value: None
228+
Accept pipeline input: False
229+
Accept wildcard characters: False
230+
```
231+
232+
### -PullIdentity
233+
AAD identity used for pull based deployments. 'system' will use the app's system assigned identy. An user assigned identity can be used by providing the client ID. Only available for Windows WebApps. Support for Linux WebApps coming soon.
234+
235+
```yaml
236+
Type: System.String
237+
Parameter Sets: (All)
238+
Aliases:
239+
240+
Required: False
241+
Position: Named
242+
Default value: None
243+
Accept pipeline input: False
244+
Accept wildcard characters: False
245+
```
246+
202247
### -Reset
203248
Reset Java web apps to default parking page
204249

0 commit comments

Comments
 (0)