Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Websites/Websites.Test/ScenarioTests/WebAppTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ public void TestPublishWebAppOneDeploy()
TestRunner.RunTestScript("Test-PublishAzureWebAppOneDeploy");
}

[Fact]
[Trait(Category.RunType, Category.LiveOnly)]
public void TestPublishWebAppOneDeployPullWithMSI()
{
TestRunner.RunTestScript("Test-PublishAzureWebAppPullWithMSI");
}

[Fact]
[Trait(Category.AcceptanceType, Category.CheckIn)]
public void TestCloneNewWebApp()
Expand Down
41 changes: 41 additions & 0 deletions src/Websites/Websites.Test/ScenarioTests/WebAppTests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,47 @@ function Test-PublishAzureWebAppOnedeploy
}
}

# New tests for PublishAzureWebApp to test pull based deployment with MSI
function Test-PublishAzureWebAppPullWithMSI
{
# Setup
$rgname = Get-ResourceGroupName
$appName = Get-WebsiteName
$location = Get-WebLocation
$planName = Get-WebHostPlanName
$tier = "Shared"

try
{
#Setup
New-AzureRmResourceGroup -Name $rgname -Location $location
$serverFarm = New-AzureRmAppServicePlan -ResourceGroupName $rgname -Name $planName -Location $location -Tier $tier

# Create new web app
$webapp = New-AzureRmWebApp -ResourceGroupName $rgname -Name $appName -Location $location -AppServicePlan $planName

#Configuring jdk and web container
# Set Java runtime to 1.8 | Tomcat. In order to deploy war, site should be configured to run with stack = TOMCAT
# or JBOSSEAP (only availble on Linux). In this test case, it creates Windows app.
$javaVersion="1.8"
$javaContainer="TOMCAT"
$javaContainerVersion="8.5"
$PropertiesObject = @{javaVersion = $javaVersion;javaContainer = $javaContainer;javaContainerVersion = $javaContainerVersion}
New-AzResource -PropertyObject $PropertiesObject -ResourceGroupName $rgname -ResourceType Microsoft.Web/sites/config -ResourceName "$appName/web" -ApiVersion 2018-02-01 -Force

$warPath = Join-Path $ResourcesPath "HelloJava.war"
$publishedApp = Publish-AzWebApp -ResourceGroupName:$rgname -Name:$appName -ArchivePath:$warPath -Type:war -Clean:$true -TargetPath:/home/site/wwwroot/webapps/ROOT -Force

Assert-NotNull $publishedApp
}
finally
{
# Cleanup
Remove-AzureRmResourceGroup -Name $rgname -Force
}

}

<#
.SYNOPSIS
Tests creating a web app with a simple parameterset.
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Websites/Websites/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- Additional information about change #1
-->
## Upcoming Release
* Add support for pull based deployments from a URL with MSI authentication in `Publish-AzWebApp`

## Version 3.3.1
* Migrated Websites.Helper generation from autorest csharp to autorest powershell.
Expand Down
73 changes: 63 additions & 10 deletions src/Websites/Websites/Cmdlets/WebApps/PublishAzureWebApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.Azure.Commands.WebApps.Models;
using Microsoft.Azure.Management.WebSites.Models;
using Microsoft.WindowsAzure.Commands.Utilities.Common;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
Expand All @@ -40,10 +41,12 @@ public class PublishAzureWebAppCmdlet : WebAppOptionalSlotBaseCmdlet
// Poll status for a maximum of 35 minutes (2100 seconds / 2 seconds per status check)
private const int NumStatusChecks = 1050;

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

[Parameter(Mandatory = false, HelpMessage = "URL of the artifact. The webapp will pull the artifact from this URL. Ex: \"http://mysite.com/files/myapp.war")]
public string ArchiveURL { get; set; }

[Parameter(Mandatory = false, HelpMessage = "Used to override the type of artifact being deployed.")]
[ValidateSet("war", "jar", "ear", "zip", "static")]
public string Type { get; set; }
Expand All @@ -60,6 +63,9 @@ public class PublishAzureWebAppCmdlet : WebAppOptionalSlotBaseCmdlet
[Parameter(Mandatory = false, HelpMessage = "Absolute path that the artifact should be deployed to.")]
public string TargetPath { get; set; }

[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.")]
public string PullIdentity { get; set; }

[Parameter(Mandatory = false, HelpMessage = "Disables any language-specific defaults")]
public SwitchParameter IgnoreStack { get; set; }

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

PSSite app = new PSSite(WebsitesClient.GetWebApp(ResourceGroupName, Name, Slot));
bool isLinuxApp = app.Kind != null && app.Kind.ToLower().Contains("linux");

HttpResponseMessage r;
string deploymentStatusUrl = user.ScmUri + "/api/deployments/latest";

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

if (string.IsNullOrEmpty(ArchivePath) && string.IsNullOrEmpty(ArchiveURL))
{
var rec = new ErrorRecord(new Exception("Either ArchivePath or ArchiveURL need to be provided."), string.Empty, ErrorCategory.InvalidArgument, null);
WriteError(rec);
return;
}

if (!string.IsNullOrEmpty(ArchiveURL) && string.IsNullOrEmpty(Type))
{
var rec = new ErrorRecord(new Exception("Deployment type is mandatory when deploying from URLs. Use -type"), string.Empty, ErrorCategory.InvalidArgument, null);
WriteError(rec);
return;
}

if (!string.IsNullOrEmpty(PullIdentity) && isLinuxApp)
{
var rec = new ErrorRecord(new Exception("Pull with MSI support is not yet available for Linux webapps"), string.Empty, ErrorCategory.InvalidArgument, null);
WriteError(rec);
return;
}

if (!string.IsNullOrEmpty(Type))
{
paramValues["type"] = Type;
Expand Down Expand Up @@ -157,11 +187,8 @@ public override void ExecuteCmdlet()

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

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

HttpContent fileContent = new StreamContent(s);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
r = client.PostAsync(deployUrl, fileContent).Result;
HttpContent archiveContent = null;

// pull based deployment
if (!string.IsNullOrEmpty(ArchiveURL) && string.IsNullOrEmpty(ArchivePath))
{
archiveContent = new StringContent(JsonConvert.SerializeObject(new { packageUri = ArchiveURL, pullIdentity = PullIdentity }));
archiveContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
r = client.PostAsync(deployUrl, archiveContent).Result;
}
//push based deployment
else if (string.IsNullOrEmpty(ArchiveURL) && !string.IsNullOrEmpty(ArchivePath))
{
if (!Path.IsPathRooted(ArchivePath))
{
ArchivePath = Path.Combine(this.SessionState.Path.CurrentFileSystemLocation.Path, ArchivePath);
}
using (var s = File.OpenRead(ArchivePath))
{
archiveContent = new StreamContent(s);
archiveContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
r = client.PostAsync(deployUrl, archiveContent).Result;
}
}
else
{
var rec = new ErrorRecord(new Exception("Could not find artifact source"), string.Empty, ErrorCategory.InvalidArgument, null);
WriteError(rec);
return;
}

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

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

PSSite app = new PSSite(WebsitesClient.GetWebApp(ResourceGroupName, Name, Slot));
WriteObject(app);
}
}
Expand Down
63 changes: 54 additions & 9 deletions src/Websites/Websites/help/Publish-AzWebApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ Deploys an Azure Web App from a ZIP, JAR, or WAR file using zipdeploy.

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

### FromResourceName
```
Publish-AzWebApp -ArchivePath <String> [-Type <String>] [-Clean] [-Async] [-Restart] [-TargetPath <String>]
[-IgnoreStack] [-Reset] [-Force] [-AsJob] [-Timeout <Double>] [-ResourceGroupName] <String> [-Name] <String>
[[-Slot] <String>] [-DefaultProfile <IAzureContextContainer>] [-WhatIf]
[-Confirm] [<CommonParameters>]
Publish-AzWebApp [-ArchivePath <String>] [-ArchiveURL <String>] [-Type <String>] [-Clean] [-Async] [-Restart]
[-TargetPath <String>] [-PullIdentity <String>] [-IgnoreStack] [-Reset] [-Force] [-AsJob] [-Timeout <Double>]
[-ResourceGroupName] <String> [-Name] <String> [[-Slot] <String>] [-DefaultProfile <IAzureContextContainer>]
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

## DESCRIPTION
Expand Down Expand Up @@ -87,7 +87,22 @@ Type: System.String
Parameter Sets: (All)
Aliases:

Required: True
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -ArchiveURL
URL of the artifact. The webapp will pull the artifact from this URL. Ex: "http://mysite.com/files/myapp.war

```yaml
Type: System.String
Parameter Sets: (All)
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Expand Down Expand Up @@ -199,6 +214,36 @@ Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
```

### -ProgressAction
{{ Fill ProgressAction Description }}

```yaml
Type: System.Management.Automation.ActionPreference
Parameter Sets: (All)
Aliases: proga

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -PullIdentity
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.

```yaml
Type: System.String
Parameter Sets: (All)
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -Reset
Reset Java web apps to default parking page

Expand Down