3

I'm in the process of getting a deployment that targets Azure App Service automated and I'm using FTP deployment.

Unfortunately, the hostnames for FTP deployment seem different from app to app and I'd like my scripts to be able to infer the correct one themselves.

Is there anything in the azure CLI that can get me the FTP hostname value? I'm using 2.0 (the shiny new one) if that helps at all.

Tom Sun - MSFT
  • 24,161
  • 3
  • 30
  • 47
Alexander Trauzzi
  • 7,277
  • 13
  • 68
  • 112

3 Answers3

1

Base on my knowledge, there is no Azure CLI to get FTP hostname value. But we could get the hostname by using Azure Rest API ListPublishingProfileXmlWithSecrets to get the Azure WebApp FTP hostname from the PublishingProfile.

If PowerShell is possible for your case, we also can use PowerShell command Get-AzureRmWebAppPublishingProfile to get the Azure Website PublishProfile.

If FTP is not the single choice to deploy the Azue WebApp, We also can use Kudu API to deploy the Azure Website. We can get the deployment credentials easily with CLI command azure webapp publishingprofile show resourcegroup WebAppname.

enter image description here

Update:

For Azure CLI 2.0 az command, we could use az appservice web deployment list-site-credentials to get the website deployment credentials. More command please refer to document.

az appservice web deployment list-site-credentials --resource-group resourcegroup --name WebAppname

enter image description here

Tom Sun - MSFT
  • 24,161
  • 3
  • 30
  • 47
  • I'm on unix and running in a build container, deploying a nodejs app. I'd rather stick with bash for my shell scripts. Is there a nicer front end to the Kudu zip APIs? This seems like something that would be nice to have as part of `az`. – Alexander Trauzzi Mar 06 '17 at 13:23
  • 1
    Is there an equivalent to what you shared here with the new `az` command? `webapp publishingprofile show` doesn't seem to exist anymore? – Alexander Trauzzi Mar 07 '17 at 03:02
  • 2
    Please have a try to use the command `az appservice web deployment list-site-credentials --resource-group resourcegroup --name WebAppname` to get the credentials, I have updated the answer. – Tom Sun - MSFT Mar 07 '17 at 05:55
  • Hmm, I don't seem to be getting any deployment URIs when I run that. – Alexander Trauzzi Mar 10 '17 at 00:06
  • The deployment URI `https://user:password@{sitename}.scm.azurewebsites.net/api/zip/{path}/`. About how to use the kudu API to deploy website, you can refer to the [blog](https://cmatskas.com/deploying-azure-functions-with-arm-templates-and-the-kudu-rest-api/) – Tom Sun - MSFT Mar 10 '17 at 01:03
  • What should I use for username and password? I've created a special application principal for my deployment server... – Alexander Trauzzi Mar 10 '17 at 03:05
  • By using `az appservice web deployment list-site-credentials` command, we can get the publishingusername and publishingpassword. – Tom Sun - MSFT Mar 10 '17 at 03:32
  • Right, I get that. But the username and password are coming back null. – Alexander Trauzzi Mar 10 '17 at 12:10
  • please use publishingusername and publishpassword for deployment uri. – Tom Sun - MSFT Mar 10 '17 at 12:40
  • Those are the ones coming back as null. – Alexander Trauzzi Mar 10 '17 at 13:42
1

I wouldn't recommend doing this in production, but it works. It's very hacky, so beware of its fragility:

$ az appservice web show -n ${WEB_APP} -g ${RES_GRP} -o json --query "hostNames" |
    grep -v '.scm.' |
    grep azurewebsites.net | # Drop custom domains here
    xargs host | # Resolve the scale unit name
    grep cloudapp.net |
    tail -1 |
    cut -d'.' -f 1

Output:

waws-prod-am2-103

That represents the scale unit the App Service Plan lives in. Since there seems to be a 1:1 relationship between the scale unit and FTP endpoint, you may construct the FTP FQDN like this:

waws-prod-am2-103.ftp.azurewebsites.windows.net

That being said, again, i would advise AGAINST doing that and using Kudu's Zip Upload APIs instead (if you really don't want to / can't deploy from source control). Tom Sun - MSFT already recommended this approach, and i'm with him on that one.

evilSnobu
  • 24,582
  • 8
  • 41
  • 71
  • Honestly, although you don't recommend it, this almost seems like the preferable approach because it doesn't require me to customize around a proprietary Kudu API. I definitely don't want to be using git for deployment as it ends up requiring kludges and creates a bunch of other problems for my deployment scripts. – Alexander Trauzzi Mar 06 '17 at 13:26
  • 1
    WebDeploy would be another option (since App Service supports that on the .scm. endpoint), but in my opinion it's far more complex than calling PUT on Kudu's **zip** controller. – evilSnobu Mar 06 '17 at 15:45
  • WebDeploy is Windows only too, isn't it? Either way, I'm realizing what you're saying makes a ton of sense. So I'm going to try to just zap the files to the kudu endpoint. Failing that, I always know there's this FTP option. – Alexander Trauzzi Mar 06 '17 at 17:07
  • 1
    Windows only, and it always gives me chills when i look at that syntax. That's why i recommended it last. – evilSnobu Mar 07 '17 at 09:27
0

For those using the Azure JavaScript SDK, you can use this snippet to get the Site Credentials (aka Publish Profile Credentials). I'm assuming you have an instance of WebSiteManagementClient and an implementation of streamToBuffer which converts a nodejs ReadableStream into a Buffer.

let credentialCreate = await webSiteManagementClient.webApps.listPublishingProfileXmlWithSecrets(
  resourceGroupName, // The resource group the Function App is in
  siteName,          // i.e. the Function App name
  {
    format: 'FileZilla3'
  }
)

let contentBuffer = await streamToBuffer(credentialCreate.readableStreamBody);
let credentialsAsXML = contentBuffer.toString();

And for a quick and dirty way to parse Host, User, and Pass...

let hostResults = credentialsAsXML.match(/<Host>(.+)<\/Host>/)
let userResults = credentialsAsXML.match(/<User>(.+)<\/User>/)
let passResults = credentialsAsXML.match(/<Pass(?: encoding="base64")?>(.+)<\/Pass>/)
let host = hostResults ? hostResults[1] : null;
let user = userResults ? userResults[1] : null;
let pass = passResults ? passResults[1] : null;
console.log(host, user, pass)

And finally, if you intend to get the "Profile Publishing Credentials" you'll need to further parse the User and Pass values.

// The "User" value in the XML is structured as: my-new-function3\$my-new-function3.
// The actual user that needs to be used in the Zip Endpoint is the last segment starting
// with the dollar sign
let ppcUser = user.replace(siteName + '\\', '');

// The "Pass" value in the XML is base64 encoded. We need to decode this before
// we use it on the Zip endpoint.
let ppcDecodedPass = Buffer.from(pass, 'base64').toString();
Albtzrly
  • 924
  • 1
  • 6
  • 15