PageRenderTime 9ms CodeModel.GetById 2ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 1ms

/src/ServiceManagement/Services/Commands.Utilities/Websites/WebsitesClient.cs

https://github.com/elastacloud/azure-sdk-tools
C# | 1528 lines | 922 code | 166 blank | 440 comment | 74 complexity | 4a394ae1c63a1936df83bfd13203dfc6 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// ----------------------------------------------------------------------------------
   2//
   3// Copyright Microsoft Corporation
   4// Licensed under the Apache License, Version 2.0 (the "License");
   5// you may not use this file except in compliance with the License.
   6// You may obtain a copy of the License at
   7// http://www.apache.org/licenses/LICENSE-2.0
   8// Unless required by applicable law or agreed to in writing, software
   9// distributed under the License is distributed on an "AS IS" BASIS,
  10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11// See the License for the specific language governing permissions and
  12// limitations under the License.
  13// ----------------------------------------------------------------------------------
  14
  15namespace Microsoft.WindowsAzure.Commands.Utilities.Websites
  16{
  17    using System;
  18    using System.Collections;
  19    using System.Collections.Generic;
  20    using System.Diagnostics;
  21    using System.IO;
  22    using System.Linq;
  23    using System.Net;
  24    using System.Net.Http;
  25    using System.Threading;
  26    using System.Web;
  27    using System.Xml.Linq;
  28    using CloudService;
  29    using Management.WebSites;
  30    using Management.WebSites.Models;
  31    using Microsoft.Build.Evaluation;
  32    using Microsoft.Build.Framework;
  33    using Microsoft.Build.Logging;
  34    using Microsoft.Web.Deployment;
  35    using Microsoft.WindowsAzure.Commands.Utilities.Websites.Services.WebJobs;
  36    using Microsoft.WindowsAzure.Commands.Websites.WebJobs;
  37    using Microsoft.WindowsAzure.WebSitesExtensions;
  38    using Microsoft.WindowsAzure.WebSitesExtensions.Models;
  39    using Newtonsoft.Json.Linq;
  40    using Properties;
  41    using Services;
  42    using Services.DeploymentEntities;
  43    using Services.WebEntities;
  44    using Utilities.Common;
  45
  46    public class WebsitesClient : IWebsitesClient
  47    {
  48        private const int UploadJobWaitTime = 2000;
  49
  50        private readonly CloudServiceClient cloudServiceClient;
  51
  52        public static string SlotFormat = "{0}({1})";
  53
  54        public IWebSiteManagementClient WebsiteManagementClient { get; internal set; }
  55
  56        public Action<string> Logger { get; set; }
  57
  58        /// <summary>
  59        /// Creates new WebsitesClient
  60        /// </summary>
  61        /// <param name="subscription">Subscription containing websites to manipulate</param>
  62        /// <param name="logger">The logger action</param>
  63        public WebsitesClient(WindowsAzureSubscription subscription, Action<string> logger)
  64        {
  65            Logger = logger;
  66            cloudServiceClient = new CloudServiceClient(subscription, debugStream: logger);
  67            WebsiteManagementClient = subscription.CreateClient<WebSiteManagementClient>();
  68        }
  69
  70        /// <summary>
  71        /// Gets website name in the current directory.
  72        /// </summary>
  73        /// <returns></returns>
  74        private string GetWebsiteFromCurrentDirectory()
  75        {
  76            return GitWebsite.ReadConfiguration().Name;
  77        }
  78
  79        private Repository GetRepository(string websiteName)
  80        {
  81            Site site = WebsiteManagementClient.GetSiteWithCache(websiteName);
  82
  83            if (site != null)
  84            {
  85                return new Repository(site);
  86            }
  87
  88            throw new Exception(Resources.RepositoryNotSetup);
  89        }
  90
  91        private HttpClient CreateDeploymentHttpClient(string websiteName)
  92        {
  93            Repository repository;
  94            ICredentials credentials;
  95            GetWebsiteDeploymentHttpConfiguration(websiteName, out repository, out credentials);
  96            return HttpClientHelper.CreateClient(repository.RepositoryUri, credentials);
  97        }
  98
  99        private string GetWebsiteDeploymentHttpConfiguration(
 100            string name,
 101            out Repository repository,
 102            out ICredentials credentials)
 103        {
 104            name = SetWebsiteName(name, null);
 105            repository = GetRepository(name);
 106            credentials = new NetworkCredential(
 107                repository.PublishingUsername,
 108                repository.PublishingPassword);
 109            return name;
 110        }
 111
 112        private string GetWebsiteName(string name)
 113        {
 114            return string.IsNullOrEmpty(name) ? GetWebsiteFromCurrentDirectory() : name;
 115        }
 116
 117        private void ChangeWebsiteState(string name, string webspace, WebsiteState state)
 118        {
 119            WebsiteManagementClient.WebSites.Update(webspace, name, new WebSiteUpdateParameters
 120            {
 121                State = state.ToString(),
 122                // Set the following 3 collection properties to null since by default they are empty lists,
 123                // which will clear the corresponding settings of the web site, thus results in a 404 when browsing the web site.
 124                HostNames = null,
 125                HostNameSslStates = null,
 126                SslCertificates = null
 127            });
 128        }
 129
 130        private void SetApplicationDiagnosticsSettings(
 131            string name,
 132            WebsiteDiagnosticOutput output,
 133            bool setFlag,
 134            Dictionary<DiagnosticProperties, object> properties = null)
 135        {
 136            Site website = GetWebsite(name);
 137
 138            using (HttpClient client = CreateDeploymentHttpClient(website.Name))
 139            {
 140                DiagnosticsSettings diagnosticsSettings = GetApplicationDiagnosticsSettings(website.Name);
 141                switch (output)
 142                {
 143                    case WebsiteDiagnosticOutput.FileSystem:
 144                        diagnosticsSettings.AzureDriveTraceEnabled = setFlag;
 145                        diagnosticsSettings.AzureDriveTraceLevel = setFlag ?
 146                        (Services.DeploymentEntities.LogEntryType)properties[DiagnosticProperties.LogLevel] :
 147                        diagnosticsSettings.AzureDriveTraceLevel;
 148                        break;
 149
 150                    case WebsiteDiagnosticOutput.StorageTable:
 151                        diagnosticsSettings.AzureTableTraceEnabled = setFlag;
 152                        if (setFlag)
 153                        {
 154                            const string storageTableName = "CLOUD_STORAGE_ACCOUNT";
 155                            string storageAccountName = (string)properties[DiagnosticProperties.StorageAccountName];
 156                            string connectionString = cloudServiceClient.GetStorageServiceConnectionString(
 157                                storageAccountName);
 158                            SetConnectionString(website.Name, storageTableName, connectionString, DatabaseType.Custom);
 159
 160                            diagnosticsSettings.AzureTableTraceLevel = setFlag ?
 161                                (Services.DeploymentEntities.LogEntryType)properties[DiagnosticProperties.LogLevel] :
 162                                diagnosticsSettings.AzureTableTraceLevel;
 163                        }
 164                        break;
 165
 166                    default:
 167                        throw new ArgumentException();
 168                }
 169
 170                JObject json = new JObject(
 171                    new JProperty(UriElements.AzureDriveTraceEnabled, diagnosticsSettings.AzureDriveTraceEnabled),
 172                    new JProperty(UriElements.AzureDriveTraceLevel, diagnosticsSettings.AzureDriveTraceLevel.ToString()),
 173                    new JProperty(UriElements.AzureTableTraceEnabled, diagnosticsSettings.AzureTableTraceEnabled),
 174                    new JProperty(UriElements.AzureTableTraceLevel, diagnosticsSettings.AzureTableTraceLevel.ToString()));
 175                client.PostJson(UriElements.DiagnosticsSettings, json, Logger);
 176            }
 177        }
 178
 179        private void SetSiteDiagnosticsSettings(
 180            string name,
 181            bool webServerLogging,
 182            bool detailedErrorMessages,
 183            bool failedRequestTracing,
 184            bool setFlag)
 185        {
 186            Site website = GetWebsite(name);
 187
 188            var update = WebsiteManagementClient.WebSites.GetConfiguration(website.WebSpace, website.Name).ToUpdate();
 189            update.HttpLoggingEnabled = webServerLogging ? setFlag : update.HttpLoggingEnabled;
 190            update.DetailedErrorLoggingEnabled = detailedErrorMessages ? setFlag : update.DetailedErrorLoggingEnabled;
 191            update.RequestTracingEnabled = failedRequestTracing ? setFlag : update.RequestTracingEnabled;
 192
 193            WebsiteManagementClient.WebSites.UpdateConfiguration(website.WebSpace, website.Name, update);
 194        }
 195
 196        private bool IsProductionSlot(string slot)
 197        {
 198            return (!string.IsNullOrEmpty(slot)) &&
 199                (slot.Equals(WebsiteSlotName.Production.ToString(), StringComparison.OrdinalIgnoreCase));
 200        }
 201
 202        private IWebSiteExtensionsClient GetWebSiteExtensionsClient(string websiteName)
 203        {
 204            Site website = GetWebsite(websiteName);
 205            Uri endpointUrl = new Uri("https://" + website.EnabledHostNames.First(url => url.Contains(".scm.")));
 206            return new WebSiteExtensionsClient(websiteName, GetWebSiteExtensionsCredentials(websiteName), endpointUrl)
 207                .WithHandler(new HttpRestCallLogger());
 208        }
 209
 210        private BasicAuthenticationCloudCredentials GetWebSiteExtensionsCredentials(string name)
 211        {
 212            name = SetWebsiteName(name, null);
 213            Repository repository = GetRepository(name);
 214            return new BasicAuthenticationCloudCredentials()
 215            {
 216                Username = repository.PublishingUsername,
 217                Password = repository.PublishingPassword
 218            };
 219        }
 220
 221        /// <summary>
 222        /// Starts log streaming for the given website.
 223        /// </summary>
 224        /// <param name="name">The website name</param>
 225        /// <param name="slot">The website slot name</param>
 226        /// <param name="path">The log path, by default root</param>
 227        /// <param name="message">The substring message</param>
 228        /// <param name="endStreaming">Predicate to end streaming</param>
 229        /// <param name="waitInternal">The fetch wait interval</param>
 230        /// <returns>The log line</returns>
 231        public IEnumerable<string> StartLogStreaming(
 232            string name,
 233            string slot,
 234            string path,
 235            string message,
 236            Predicate<string> endStreaming,
 237            int waitInternal)
 238        {
 239            name = SetWebsiteName(name, slot);
 240            return StartLogStreaming(name, path, message, endStreaming, waitInternal);
 241        }
 242
 243        /// <summary>
 244        /// List log paths for a given website.
 245        /// </summary>
 246        /// <param name="name">The website name</param>
 247        /// <param name="slot">The website slot name</param>
 248        /// <returns>The list of log paths</returns>
 249        public List<LogPath> ListLogPaths(string name, string slot)
 250        {
 251            name = SetWebsiteName(name, slot);
 252            return ListLogPaths(name);
 253        }
 254
 255        /// <summary>
 256        /// Starts log streaming for the given website.
 257        /// </summary>
 258        /// <param name="name">The website name</param>
 259        /// <param name="path">The log path, by default root</param>
 260        /// <param name="message">The substring message</param>
 261        /// <param name="endStreaming">Predicate to end streaming</param>
 262        /// <param name="waitInterval">The fetch wait interval</param>
 263        /// <returns>The log line</returns>
 264        public IEnumerable<string> StartLogStreaming(
 265            string name,
 266            string path,
 267            string message,
 268            Predicate<string> endStreaming,
 269            int waitInterval)
 270        {
 271            Repository repository;
 272            ICredentials credentials;
 273            name = GetWebsiteDeploymentHttpConfiguration(name, out repository, out credentials);
 274            path = HttpUtility.UrlEncode(path);
 275            message = HttpUtility.UrlEncode(message);
 276
 277            RemoteLogStreamManager manager = new RemoteLogStreamManager(
 278                repository.RepositoryUri,
 279                path,
 280                message,
 281                credentials,
 282                Logger);
 283
 284            using (LogStreamWaitHandle logHandler = new LogStreamWaitHandle(manager.GetStream().Result))
 285            {
 286                bool doStreaming = true;
 287
 288                while (doStreaming)
 289                {
 290                    string line = logHandler.WaitNextLine(waitInterval);
 291
 292                    if (line != null)
 293                    {
 294                        yield return line;
 295                    }
 296
 297                    doStreaming = endStreaming == null || endStreaming(line);
 298                }
 299            }
 300        }
 301
 302        /// <summary>
 303        /// List log paths for a given website.
 304        /// </summary>
 305        /// <param name="name"></param>
 306        /// <returns></returns>
 307        public List<LogPath> ListLogPaths(string name)
 308        {
 309            using (HttpClient client = CreateDeploymentHttpClient(name))
 310            {
 311                return client.GetJson<List<LogPath>>(UriElements.LogPaths, Logger);
 312            }
 313        }
 314
 315        /// <summary>
 316        /// Gets the application diagnostics settings
 317        /// </summary>
 318        /// <param name="name">The website name</param>
 319        /// <returns>The website application diagnostics settings</returns>
 320        public DiagnosticsSettings GetApplicationDiagnosticsSettings(string name)
 321        {
 322            using (HttpClient client = CreateDeploymentHttpClient(name))
 323            {
 324                return client.GetJson<DiagnosticsSettings>(UriElements.DiagnosticsSettings, Logger);
 325            }
 326        }
 327
 328        /// <summary>
 329        /// Gets the application diagnostics settings
 330        /// </summary>
 331        /// <param name="name">The website name</param>
 332        /// <param name="slot">The website slot name</param>
 333        /// <returns>The website application diagnostics settings</returns>
 334        public DiagnosticsSettings GetApplicationDiagnosticsSettings(string name, string slot)
 335        {
 336            name = SetWebsiteName(name, slot);
 337            return GetApplicationDiagnosticsSettings(name);
 338        }
 339
 340        /// <summary>
 341        /// Restarts a website.
 342        /// </summary>
 343        /// <param name="name">The website name</param>
 344        public void RestartWebsite(string name)
 345        {
 346            Site website = GetWebsite(name);
 347            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Stopped);
 348            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Running);
 349        }
 350
 351        /// <summary>
 352        /// Starts a website.
 353        /// </summary>
 354        /// <param name="name">The website name</param>
 355        public void StartWebsite(string name)
 356        {
 357            Site website = GetWebsite(name);
 358            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Running);
 359        }
 360
 361        /// <summary>
 362        /// Stops a website.
 363        /// </summary>
 364        /// <param name="name">The website name</param>
 365        public void StopWebsite(string name)
 366        {
 367            Site website = GetWebsite(name);
 368            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Stopped);
 369        }
 370
 371        /// <summary>
 372        /// Restarts a website.
 373        /// </summary>
 374        /// <param name="name">The website name</param>
 375        /// <param name="slot">The website slot name</param>
 376        public void RestartWebsite(string name, string slot)
 377        {
 378            Site website = GetWebsite(name, slot);
 379            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Stopped);
 380            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Running);
 381        }
 382
 383        /// <summary>
 384        /// Starts a website.
 385        /// </summary>
 386        /// <param name="name">The website name</param>
 387        /// <param name="slot">The website slot name</param>
 388        public void StartWebsite(string name, string slot)
 389        {
 390            Site website = GetWebsite(name, slot);
 391            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Running);
 392        }
 393
 394        /// <summary>
 395        /// Stops a website.
 396        /// </summary>
 397        /// <param name="name">The website name</param>
 398        /// <param name="slot">The website slot name</param>
 399        public void StopWebsite(string name, string slot)
 400        {
 401            Site website = GetWebsite(name, slot);
 402            ChangeWebsiteState(website.Name, website.WebSpace, WebsiteState.Stopped);
 403        }
 404
 405        public WebsiteInstance[] ListWebsiteInstances(string webSpace, string fullName)
 406        {
 407            IList<string> instanceIds = WebsiteManagementClient.WebSites.GetInstanceIds(webSpace, fullName).InstanceIds;
 408            return instanceIds.Select(s => new WebsiteInstance {InstanceId = s}).ToArray();
 409        }
 410
 411        /// <summary>
 412        /// Gets a website slot instance.
 413        /// </summary>
 414        /// <param name="name">The website name</param>
 415        /// <param name="slot">The slot name</param>
 416        /// <returns>The website instance</returns>
 417        public Site GetWebsite(string name, string slot)
 418        {
 419            name = SetWebsiteName(name, slot);
 420            return GetWebsite(name);
 421        }
 422
 423        /// <summary>
 424        /// Gets a website instance.
 425        /// </summary>
 426        /// <param name="name">The website name</param>
 427        /// <returns>The website instance</returns>
 428        public Site GetWebsite(string name)
 429        {
 430            name = SetWebsiteName(name, null);
 431
 432            Site website = WebsiteManagementClient.GetSiteWithCache(name);
 433
 434            if (website == null)
 435            {
 436                throw new CloudException(string.Format(Resources.InvalidWebsite, name));
 437            }
 438
 439            return website;
 440        }
 441
 442        /// <summary>
 443        /// Gets all slots for a website
 444        /// </summary>
 445        /// <param name="name">The website name</param>
 446        /// <returns>The website slots list</returns>
 447        public List<Site> GetWebsiteSlots(string name)
 448        {
 449            name = SetWebsiteName(name, null);
 450            return ListWebsites()
 451                .Where(s =>
 452                    s.Name.IndexOf(string.Format("{0}(", name), StringComparison.OrdinalIgnoreCase) >= 0 ||
 453                    s.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
 454                .ToList();
 455        }
 456
 457        /// <summary>
 458        /// Lists all websites under the current subscription
 459        /// </summary>
 460        /// <returns>List of websites</returns>
 461        public List<Site> ListWebsites()
 462        {
 463            return ListWebSpaces().SelectMany(space => ListSitesInWebSpace(space.Name)).ToList();
 464        }
 465
 466        /// <summary>
 467        /// Lists all websites with the provided slot name.
 468        /// </summary>
 469        /// <param name="slot">The slot name</param>
 470        /// <returns>The list if websites</returns>
 471        public List<Site> ListWebsites(string slot)
 472        {
 473            return ListWebsites().Where(s => s.Name.Contains(string.Format("({0})", slot))).ToList();
 474        }
 475
 476        /// <summary>
 477        /// Gets the hostname of the website
 478        /// </summary>
 479        /// <param name="name">The website name</param>
 480        /// <param name="slot">The website slot name</param>
 481        /// <returns>The hostname</returns>
 482        public string GetHostName(string name, string slot)
 483        {
 484            slot = string.IsNullOrEmpty(slot) ? GetSlotName(name) : slot;
 485            name = SetWebsiteName(name, slot);
 486            string hostname = null;
 487            string dnsSuffix = GetWebsiteDnsSuffix();
 488
 489            if (!string.IsNullOrEmpty(slot) &&
 490                !slot.Equals(WebsiteSlotName.Production.ToString(), StringComparison.OrdinalIgnoreCase))
 491            {
 492                hostname = string.Format("{0}-{1}.{2}", GetWebsiteNameFromFullName(name), slot, dnsSuffix);
 493            }
 494            else
 495            {
 496                hostname = string.Format("{0}.{1}", name, dnsSuffix);
 497            }
 498
 499            return hostname;
 500        }
 501
 502        /// <summary>
 503        /// Create a new website.
 504        /// </summary>
 505        /// <param name="webspaceName">Web space to create site in.</param>
 506        /// <param name="siteToCreate">Details about the site to create.</param>
 507        /// <param name="slot">The slot name.</param>
 508        /// <returns>The created site object</returns>
 509        public Site CreateWebsite(string webspaceName, SiteWithWebSpace siteToCreate, string slot)
 510        {
 511            siteToCreate.Name = SetWebsiteName(siteToCreate.Name, slot);
 512            string[] hostNames = { GetHostName(siteToCreate.Name, slot) };
 513            siteToCreate.HostNames = hostNames;
 514            return CreateWebsite(webspaceName, siteToCreate);
 515        }
 516
 517        /// <summary>
 518        /// Create a new website in production.
 519        /// </summary>
 520        /// <param name="webspaceName">Web space to create site in.</param>
 521        /// <param name="siteToCreate">Details about the site to create.</param>
 522        /// <returns>The created site object</returns>
 523        private Site CreateWebsite(string webspaceName, SiteWithWebSpace siteToCreate)
 524        {
 525            var options = new WebSiteCreateParameters
 526            {
 527                Name = siteToCreate.Name,
 528                WebSpaceName = siteToCreate.WebSpaceToCreate.Name,
 529                WebSpace = new WebSiteCreateParameters.WebSpaceDetails
 530                {
 531                    GeoRegion = siteToCreate.WebSpaceToCreate.GeoRegion,
 532                    Name = siteToCreate.WebSpaceToCreate.Name,
 533                    Plan = siteToCreate.WebSpaceToCreate.Plan
 534                }
 535            };
 536
 537            siteToCreate.HostNames.ForEach(s => options.HostNames.Add(s));
 538
 539            var response = WebsiteManagementClient.WebSites.Create(webspaceName, options);
 540            return response.WebSite.ToSite();
 541        }
 542
 543        /// <summary>
 544        /// Update the set of host names for a website.
 545        /// </summary>
 546        /// <param name="site">The site name.</param>
 547        /// <param name="hostNames">The new host names.</param>
 548        public void UpdateWebsiteHostNames(Site site, IEnumerable<string> hostNames)
 549        {
 550            var update = new WebSiteUpdateParameters();
 551            foreach (var name in hostNames)
 552            {
 553                update.HostNames.Add(name);
 554            }
 555
 556            WebsiteManagementClient.WebSites.Update(site.WebSpace, site.Name, update);
 557        }
 558
 559        /// <summary>
 560        /// Update the set of host names for a website slot.
 561        /// </summary>
 562        /// <param name="site">The website name.</param>
 563        /// <param name="hostNames">The new host names.</param>
 564        /// <param name="slot">The website slot name.</param>
 565        public void UpdateWebsiteHostNames(Site site, IEnumerable<string> hostNames, string slot)
 566        {
 567            site.Name = SetWebsiteName(site.Name, slot);
 568
 569            UpdateWebsiteHostNames(site, hostNames);
 570        }
 571
 572        /// <summary>
 573        /// Gets the website configuration.
 574        /// </summary>
 575        /// <param name="name">The website name</param>
 576        /// <returns>The website configuration object</returns>
 577        public SiteConfig GetWebsiteConfiguration(string name)
 578        {
 579            Site website = GetWebsite(name);
 580            SiteConfig configuration =
 581                WebsiteManagementClient.WebSites.GetConfiguration(website.WebSpace, website.Name).ToSiteConfig();
 582
 583            return configuration;
 584        }
 585
 586        /// <summary>
 587        /// Gets a website slot configuration
 588        /// </summary>
 589        /// <param name="name">The website name</param>
 590        /// <param name="slot">The website slot name</param>
 591        /// <returns>The website cobfiguration object</returns>
 592        public SiteConfig GetWebsiteConfiguration(string name, string slot)
 593        {
 594            Site website = GetWebsite(name);
 595            website.Name = SetWebsiteName(website.Name, slot);
 596            return GetWebsiteConfiguration(website.Name);
 597        }
 598
 599        /// <summary>
 600        /// Get the real website name.
 601        /// </summary>
 602        /// <param name="name">The website name from the -Name parameter.</param>
 603        /// <param name="slot">The website name from the -Slot parameter.</param>
 604        /// <returns>The real website name.</returns>
 605        private string SetWebsiteName(string name, string slot)
 606        {
 607            name = GetWebsiteName(name);
 608            slot = slot ?? GetSlotName(name);
 609
 610            if (string.IsNullOrEmpty(slot) ||
 611                slot.Equals(WebsiteSlotName.Production.ToString(), StringComparison.OrdinalIgnoreCase))
 612            {
 613                return GetWebsiteNameFromFullName(name);
 614            }
 615            else if (name.Contains('(') && name.Contains(')'))
 616            {
 617                string currentSlot = GetSlotName(name);
 618                if (currentSlot.Equals(WebsiteSlotName.Production.ToString(), StringComparison.OrdinalIgnoreCase))
 619                {
 620                    return GetWebsiteNameFromFullName(name);
 621                }
 622
 623                return name;
 624            }
 625            else
 626            {
 627                return GetSlotDnsName(name, slot);
 628            }
 629        }
 630
 631        private string SetWebsiteNameForWebDeploy(string name, string slot)
 632        {
 633            return SetWebsiteName(name, slot).Replace("(", "__").Replace(")", string.Empty);
 634        }
 635
 636        /// <summary>
 637        /// Gets the website name without slot part
 638        /// </summary>
 639        /// <param name="name">The website full name which may include slot name</param>
 640        /// <returns>The website name</returns>
 641        public string GetWebsiteNameFromFullName(string name)
 642        {
 643            if (!string.IsNullOrEmpty(GetSlotName(name)))
 644            {
 645                name = name.Split('(')[0];
 646            }
 647
 648            return name;
 649        }
 650
 651        /// <summary>
 652        /// Update the website configuration
 653        /// </summary>
 654        /// <param name="name">The website name</param>
 655        /// <param name="newConfiguration">The website configuration object containing updates.</param>
 656        public void UpdateWebsiteConfiguration(string name, SiteConfig newConfiguration)
 657        {
 658            Site website = GetWebsite(name);
 659            WebsiteManagementClient.WebSites.UpdateConfiguration(website.WebSpace, name,
 660                newConfiguration.ToConfigUpdateParameters());
 661        }
 662
 663        /// <summary>
 664        /// Update the website slot configuration
 665        /// </summary>
 666        /// <param name="name">The website name</param>
 667        /// <param name="newConfiguration">The website configuration object containing updates.</param>
 668        /// <param name="slot">The website slot name</param>
 669        public void UpdateWebsiteConfiguration(string name, SiteConfig newConfiguration, string slot)
 670        {
 671            name = SetWebsiteName(name, slot);
 672            UpdateWebsiteConfiguration(name, newConfiguration);
 673        }
 674
 675        /// <summary>
 676        /// Create a git repository for the web site.
 677        /// </summary>
 678        /// <param name="webspaceName">Webspace that site is in.</param>
 679        /// <param name="websiteName">The site name.</param>
 680        public void CreateWebsiteRepository(string webspaceName, string websiteName)
 681        {
 682            WebsiteManagementClient.WebSites.CreateRepository(webspaceName, websiteName);
 683        }
 684
 685        /// <summary>
 686        /// Delete a website.
 687        /// </summary>
 688        /// <param name="webspaceName">webspace the site is in.</param>
 689        /// <param name="websiteName">website name.</param>
 690        /// <param name="deleteMetrics">pass true to delete stored metrics as part of removing site.</param>
 691        /// <param name="deleteEmptyServerFarm">Pass true to delete server farm is this was the last website in it.</param>
 692        public void DeleteWebsite(string webspaceName, string websiteName, bool deleteMetrics = false, bool deleteEmptyServerFarm = false)
 693        {
 694            WebSiteDeleteParameters input = new WebSiteDeleteParameters()
 695            {
 696                DeleteAllSlots = true,
 697                DeleteEmptyServerFarm = deleteEmptyServerFarm,
 698                DeleteMetrics = deleteMetrics
 699            };
 700            WebsiteManagementClient.WebSites.Delete(webspaceName, websiteName, input);
 701        }
 702
 703        /// <summary>
 704        /// Delete a website slot.
 705        /// </summary>
 706        /// <param name="webspaceName">webspace the site is in.</param>
 707        /// <param name="websiteName">website name.</param>
 708        /// <param name="slot">The website slot name</param>
 709        public void DeleteWebsite(string webspaceName, string websiteName, string slot)
 710        {
 711            slot = slot ?? GetSlotName(websiteName) ?? WebsiteSlotName.Production.ToString();
 712            websiteName = SetWebsiteName(websiteName, slot);
 713            WebSiteDeleteParameters input = new WebSiteDeleteParameters()
 714            {
 715                /**
 716                 * DeleteAllSlots is set to true in case that:
 717                 * 1) We are trying to delete a production slot and,
 718                 * 2) The website has more than one slot.
 719                 */
 720                DeleteAllSlots = IsProductionSlot(slot) && GetWebsiteSlots(websiteName).Count != 1,
 721                DeleteEmptyServerFarm = false,
 722                DeleteMetrics = false
 723            };
 724            WebsiteManagementClient.WebSites.Delete(webspaceName, websiteName, input);
 725        }
 726
 727        /// <summary>
 728        /// Get the WebSpaces.
 729        /// </summary>
 730        /// <returns>Collection of WebSpace objects</returns>
 731        public IList<WebSpace> ListWebSpaces()
 732        {
 733            return WebsiteManagementClient.WebSpaces.List().WebSpaces.Select(ws => ws.ToWebSpace()).ToList();
 734        }
 735
 736        /// <summary>
 737        /// Get the sites in the given webspace
 738        /// </summary>
 739        /// <param name="spaceName">Name of webspace</param>
 740        /// <returns>The sites</returns>
 741        public IList<Site> ListSitesInWebSpace(string spaceName)
 742        {
 743            WebSiteListParameters input = new WebSiteListParameters();
 744            input.PropertiesToInclude.Add("repositoryuri");
 745            input.PropertiesToInclude.Add("publishingpassword");
 746            input.PropertiesToInclude.Add("publishingusername");
 747            return WebsiteManagementClient.WebSpaces.ListWebSites(spaceName, input).WebSites.Select(s => s.ToSite()).ToList();
 748        }
 749
 750        /// <summary>
 751        /// Sets an AppSetting of a website.
 752        /// </summary>
 753        /// <param name="name">The website name</param>
 754        /// <param name="key">The app setting name</param>
 755        /// <param name="value">The app setting value</param>
 756        public void SetAppSetting(string name, string key, string value)
 757        {
 758            Site website = GetWebsite(name);
 759            var update = WebsiteManagementClient.WebSites.GetConfiguration(website.WebSpace, website.Name).ToUpdate();
 760
 761            update.AppSettings[name] = key;
 762
 763            WebsiteManagementClient.WebSites.UpdateConfiguration(website.WebSpace, website.Name, update);
 764        }
 765
 766        /// <summary>
 767        /// Sets a connection string for a website.
 768        /// </summary>
 769        /// <param name="name">Name of the website.</param>
 770        /// <param name="key">Connection string key.</param>
 771        /// <param name="value">Value for the connection string.</param>
 772        /// <param name="connectionStringType">Type of connection string.</param>
 773        public void SetConnectionString(string name, string key, string value, DatabaseType connectionStringType)
 774        {
 775            Site website = GetWebsite(name);
 776
 777            var update = WebsiteManagementClient.WebSites.GetConfiguration(website.WebSpace, website.Name).ToUpdate();
 778
 779            var csToUpdate = update.ConnectionStrings.FirstOrDefault(cs => cs.Name.Equals(key, StringComparison.OrdinalIgnoreCase));
 780            if (csToUpdate == null)
 781            {
 782                csToUpdate = new WebSiteUpdateConfigurationParameters.ConnectionStringInfo
 783                {
 784                    ConnectionString = value,
 785                    Name = key,
 786                    Type = connectionStringType.ToString()
 787                };
 788                update.ConnectionStrings.Add(csToUpdate);
 789            }
 790            else
 791            {
 792                csToUpdate.ConnectionString = value;
 793                csToUpdate.Type = connectionStringType.ToString();
 794            }
 795
 796            WebsiteManagementClient.WebSites.UpdateConfiguration(website.WebSpace, website.Name, update);
 797        }
 798
 799        /// <summary>
 800        /// Enables website diagnostic.
 801        /// </summary>
 802        /// <param name="name">The website name</param>
 803        /// <param name="webServerLogging">Flag for webServerLogging</param>
 804        /// <param name="detailedErrorMessages">Flag for detailedErrorMessages</param>
 805        /// <param name="failedRequestTracing">Flag for failedRequestTracing</param>
 806        public void EnableSiteDiagnostic(
 807            string name,
 808            bool webServerLogging,
 809            bool detailedErrorMessages,
 810            bool failedRequestTracing)
 811        {
 812            SetSiteDiagnosticsSettings(name, webServerLogging, detailedErrorMessages, failedRequestTracing, true);
 813        }
 814
 815        /// <summary>
 816        /// Disables site diagnostic.
 817        /// </summary>
 818        /// <param name="name">The website name</param>
 819        /// <param name="webServerLogging">Flag for webServerLogging</param>
 820        /// <param name="detailedErrorMessages">Flag for detailedErrorMessages</param>
 821        /// <param name="failedRequestTracing">Flag for failedRequestTracing</param>
 822        public void DisableSiteDiagnostic(
 823            string name,
 824            bool webServerLogging,
 825            bool detailedErrorMessages,
 826            bool failedRequestTracing)
 827        {
 828            SetSiteDiagnosticsSettings(name, webServerLogging, detailedErrorMessages, failedRequestTracing, false);
 829        }
 830
 831        /// <summary>
 832        /// Enables application diagnostic on website slot.
 833        /// </summary>
 834        /// <param name="name">The website name</param>
 835        /// <param name="output">The application log output, FileSystem or StorageTable</param>
 836        /// <param name="properties">The diagnostic setting properties</param>
 837        /// <param name="slot">The website slot name</param>
 838        public void EnableApplicationDiagnostic(
 839            string name,
 840            WebsiteDiagnosticOutput output,
 841            Dictionary<DiagnosticProperties, object> properties,
 842            string slot)
 843        {
 844            SetApplicationDiagnosticsSettings(SetWebsiteName(name, slot), output, true, properties);
 845        }
 846
 847        /// <summary>
 848        /// Disables application diagnostic.
 849        /// </summary>
 850        /// <param name="name">The website name</param>
 851        /// <param name="output">The application log output, FileSystem or StorageTable</param>
 852        /// <param name="slot">The website slot name</param>
 853        public void DisableApplicationDiagnostic(string name, WebsiteDiagnosticOutput output, string slot)
 854        {
 855            SetApplicationDiagnosticsSettings(SetWebsiteName(name, slot), output, false);
 856        }
 857
 858        /// <summary>
 859        /// Enables application diagnostic on website slot.
 860        /// </summary>
 861        /// <param name="name">The website name</param>
 862        /// <param name="output">The application log output, FileSystem or StorageTable</param>
 863        /// <param name="properties">The diagnostic setting properties</param>
 864        public void EnableApplicationDiagnostic(
 865            string name,
 866            WebsiteDiagnosticOutput output,
 867            Dictionary<DiagnosticProperties, object> properties)
 868        {
 869            SetApplicationDiagnosticsSettings(name, output, true, properties);
 870        }
 871
 872        /// <summary>
 873        /// Disables application diagnostic.
 874        /// </summary>
 875        /// <param name="name">The website name</param>
 876        /// <param name="output">The application log output, FileSystem or StorageTable</param>
 877        public void DisableApplicationDiagnostic(string name, WebsiteDiagnosticOutput output)
 878        {
 879            SetApplicationDiagnosticsSettings(name, output, false);
 880        }
 881
 882        /// <summary>
 883        /// Lists available website locations.
 884        /// </summary>
 885        /// <returns>List of location names</returns>
 886        public List<string> ListAvailableLocations()
 887        {
 888            var webspacesGeoRegions = WebsiteManagementClient.WebSpaces.List()
 889                .WebSpaces.Select(w => w.GeoRegion);
 890
 891            var availableRegionsResponse = WebsiteManagementClient.WebSpaces.ListGeoRegions();
 892
 893            return availableRegionsResponse.GeoRegions.Select(r => r.Name).Union(webspacesGeoRegions).ToList();
 894        }
 895
 896        /// <summary>
 897        /// Gets the default website DNS suffix for the current environment.
 898        /// </summary>
 899        /// <returns>The website DNS suffix</returns>
 900        public string GetWebsiteDnsSuffix()
 901        {
 902            return WebsiteManagementClient.WebSpaces.GetDnsSuffix().DnsSuffix;
 903        }
 904
 905        /// <summary>
 906        /// Gets the default location for websites.
 907        /// </summary>
 908        /// <returns>The default location name.</returns>
 909        public string GetDefaultLocation()
 910        {
 911            return ListAvailableLocations().First();
 912        }
 913
 914        /// <summary>
 915        /// Get a list of the user names configured to publish to the space.
 916        /// </summary>
 917        /// <returns>The list of user names.</returns>
 918        public IList<string> ListPublishingUserNames()
 919        {
 920            return WebsiteManagementClient.WebSpaces.ListPublishingUsers()
 921                .Users.Select(u => u.Name).Where(n => !string.IsNullOrEmpty(n)).ToList();
 922        }
 923
 924        /// <summary>
 925        /// Checks if a website exists or not.
 926        /// </summary>
 927        /// <param name="name">The website name</param>
 928        /// <returns>True if exists, false otherwise</returns>
 929        public bool WebsiteExists(string name)
 930        {
 931            Site website = null;
 932
 933            try
 934            {
 935                website = GetWebsite(name);
 936            }
 937            catch
 938            {
 939                // Ignore exception.
 940            }
 941
 942            return website != null;
 943        }
 944
 945        /// <summary>
 946        /// Checks if a website slot exists or not.
 947        /// </summary>
 948        /// <param name="name">The website name</param>
 949        /// <param name="slot">The website slot name</param>
 950        /// <returns>True if exists, false otherwise</returns>
 951        public bool WebsiteExists(string name, string slot)
 952        {
 953            Site website = null;
 954
 955            try
 956            {
 957                website = GetWebsite(name, slot);
 958            }
 959            catch
 960            {
 961                // Ignore exception.
 962            }
 963
 964            return website != null;
 965        }
 966
 967        /// <summary>
 968        /// Updates a website compute mode.
 969        /// </summary>
 970        /// <param name="websiteToUpdate">The website to update</param>
 971        public void UpdateWebsiteComputeMode(Site websiteToUpdate)
 972        {
 973            WebsiteManagementClient.WebSites.Update(
 974                websiteToUpdate.WebSpace,
 975                websiteToUpdate.Name,
 976                new WebSiteUpdateParameters
 977                {
 978                    ComputeMode = websiteToUpdate.ComputeMode,
 979                    // Set the following 3 collection properties to null since by default they are empty lists,
 980                    // which will clear the corresponding settings of the web site, thus results in a 404 when browsing the web site.
 981                    HostNames = null,
 982                    HostNameSslStates = null,
 983                    SslCertificates = null
 984                });
 985        }
 986
 987        /// <summary>
 988        /// Gets a website slot DNS name.
 989        /// </summary>
 990        /// <param name="name">The website name</param>
 991        /// <param name="slot">The slot name</param>
 992        /// <returns>the slot DNS name</returns>
 993        public string GetSlotDnsName(string name, string slot)
 994        {
 995            return string.Format(SlotFormat, name, slot);
 996        }
 997
 998        /// <summary>
 999        /// Switches the given website slot with the production slot
1000        /// </summary>
1001        /// <param name="webspaceName">The webspace name</param>
1002        /// <param name="websiteName">The website name</param>
1003        /// <param name="slot1">The website's first slot name</param>
1004        /// <param name="slot2">The website's second slot name</param>
1005        public void SwitchSlots(string webspaceName, string websiteName, string slot1, string slot2)
1006        {
1007            Debug.Assert(!string.IsNullOrEmpty(slot1));
1008            Debug.Assert(!string.IsNullOrEmpty(slot2));
1009
1010            WebsiteManagementClient.WebSites.SwapSlots(webspaceName, websiteName, slot1, slot2);
1011        }
1012
1013        /// <summary>
1014        /// Gets the slot name from the website name.
1015        /// </summary>
1016        /// <param name="name">The website name</param>
1017        /// <returns>The slot name</returns>
1018        public string GetSlotName(string name)
1019        {
1020            string slotName = null;
1021            if (!string.IsNullOrEmpty(name))
1022            {
1023                if (name.Contains('(') && name.Contains(')'))
1024                {
1025                    string[] split = name.Split('(');
1026                    slotName = split[1].TrimEnd(')').ToLower();
1027                }
1028            }
1029
1030            return slotName;
1031        }
1032
1033        /// <summary>
1034        /// Checks whether a website name is available or not.
1035        /// </summary>
1036        /// <param name="name">The website name</param>
1037        /// <returns>True means available, false otherwise</returns>
1038        public bool CheckWebsiteNameAvailability(string name)
1039        {
1040            return WebsiteManagementClient.WebSites.IsHostnameAvailable(name).IsAvailable;
1041        }
1042
1043        #region WebDeploy
1044
1045        /// <summary>
1046        /// Build a Visual Studio web project and generate a WebDeploy package.
1047        /// </summary>
1048        /// <param name="projectFile">The project file.</param>
1049        /// <param name="configuration">The configuration of the build, like Release or Debug.</param>
1050        /// <param name="logFile">The build log file if there is any error.</param>
1051        /// <returns>The full path of the generated WebDeploy package.</returns>
1052        public string BuildWebProject(string projectFile, string configuration, string logFile)
1053        {
1054            ProjectCollection pc = new ProjectCollection();
1055            Project project = pc.LoadProject(projectFile);
1056
1057            // Use a file logger to store detailed build info.
1058            FileLogger fileLogger = new FileLogger();
1059            fileLogger.Parameters = string.Format("logfile={0}", logFile);
1060            fileLogger.Verbosity = LoggerVerbosity.Diagnostic;
1061
1062            // Set the configuration used by MSBuild.
1063            project.SetProperty("Configuration", configuration);
1064
1065            // Set this property use "managedRuntimeVersion=v4.0".
1066            // Otherwise, WebDeploy will fail becasue Azure Web Site is expecting v4.0.
1067            project.SetProperty("VisualStudioVersion", "11.0");
1068
1069            // Build the project.
1070            var buildSucceed = project.Build("Package", new ILogger[] { fileLogger });
1071
1072            if (buildSucceed)
1073            {
1074                // If build succeeds, delete the build.log file since there is no use of it.
1075                File.Delete(logFile);
1076                return Path.Combine(Path.GetDirectoryName(projectFile), "obj", configuration, "Package", Path.GetFileNameWithoutExtension(projectFile) + ".zip");
1077            }
1078            else
1079            {
1080                // If build fails, tell the user to look at the build.log file.
1081                throw new Exception(string.Format(Resources.WebProjectBuildFailTemplate, logFile));
1082            }
1083        }
1084
1085        /// <summary>
1086        /// Gets the website WebDeploy publish profile.
1087        /// </summary>
1088        /// <param name="websiteName">Website name.</param>
1089        /// <param name="slot">Slot name. By default is null.</param>
1090        /// <returns>The publish profile.</returns>
1091        public WebSiteGetPublishProfileResponse.PublishProfile GetWebDeployPublishProfile(string websiteName, string slot = null)
1092        {
1093            var site = this.GetWebsite(websiteName);
1094
1095            var response = WebsiteManagementClient.WebSites.GetPublishProfile(site.WebSpace, SetWebsiteName(websiteName, slot));
1096
1097            foreach (var profile in response)
1098            {
1099                if (string.Compare(profile.PublishMethod, Resources.WebDeployKeywordInWebSitePublishProfile) == 0)
1100                {
1101                    return profile;
1102                }
1103            }
1104
1105            return null;
1106        }
1107
1108        /// <summary>
1109        /// Publish a WebDeploy package folder to a web site.
1110        /// </summary>
1111        /// <param name="websiteName">The name of the web site.</param>
1112        /// <param name="slot">The name of the slot.</param>
1113        /// <param name="package">The WebDeploy package.</param>
1114        /// <param name="connectionStrings">The connection strings to overwrite the ones in the Web.config file.</param>
1115        public void PublishWebProject(string websiteName, string slot, string package, Hashtable connectionStrings)
1116        {
1117            if (File.GetAttributes(package).HasFlag(FileAttributes.Directory))
1118            {
1119                PublishWebProjectFromPackagePath(websiteName, slot, package, connectionStrings);
1120            }
1121            else
1122            {
1123                PublishWebProjectFromPackageFile(websiteName, slot, package, connectionStrings);
1124            }
1125        }
1126
1127        /// <summary>
1128        /// Publish a WebDeploy package zip file to a web site.
1129        /// </summary>
1130        /// <param name="websiteName">The name of the web site.</param>
1131        /// <param name="slot">The name of the slot.</param>
1132        /// <param name="package">The WebDeploy package zip file.</param>
1133        /// <param name="connectionStrings">The connection strings to overwrite the ones in the Web.config file.</param>
1134        private void PublishWebProjectFromPackageFile(string websiteName, string slot, string package, Hashtable connectionStrings)
1135        {
1136            DeploymentBaseOptions remoteBaseOptions = CreateRemoteDeploymentBaseOptions(websiteName, slot);
1137            DeploymentBaseOptions localBaseOptions = new DeploymentBaseOptions();
1138
1139            DeploymentProviderOptions remoteProviderOptions = new DeploymentProviderOptions(DeploymentWellKnownProvider.Auto);
1140
1141            using (var deployment = DeploymentManager.CreateObject(DeploymentWellKnownProvider.Package, package, localBaseOptions))
1142            {
1143                DeploymentSyncParameter providerPathParameter = new DeploymentSyncParameter(
1144                    "Provider Path Parameter",
1145                    "Provider Path Parameter",
1146                    SetWebsiteNameForWebDeploy(websiteName, slot),
1147                    null);
1148                DeploymentSyncParameterEntry iisAppEntry = new DeploymentSyncParameterEntry(
1149                    DeploymentSyncParameterEntryKind.ProviderPath,
1150                    DeploymentWellKnownProvider.IisApp.ToString(),
1151                    ".*",
1152                    null);
1153                DeploymentSyncParameterEntry setAclEntry = new DeploymentSyncParameterEntry(
1154                    DeploymentSyncParameterEntryKind.ProviderPath,
1155                    DeploymentWellKnownProvider.SetAcl.ToString(),
1156                    ".*",
1157                    null);
1158                providerPathParameter.Add(iisAppEntry);
1159                providerPathParameter.Add(setAclEntry);
1160                deployment.SyncParameters.Add(providerPathParameter);
1161
1162                // Replace the connection strings in Web.config with the ones user specifies from the cmdlet.
1163                ReplaceConnectionStrings(deployment, connectionStrings);
1164
1165                DeploymentSyncOptions syncOptions = new DeploymentSyncOptions();
1166
1167                deployment.SyncTo(remoteProviderOptions, remoteBaseOptions, syncOptions);
1168            }
1169        }
1170
1171        /// <summary>
1172        /// Publish a WebDeploy package zip file to a web site.
1173        /// </summary>
1174        /// <param name="websiteName">The name of the web site.</param>
1175        /// <param name="slot">The name of the slot.</param>
1176        /// <param name="package">The WebDeploy package zip file.</param>
1177        /// <param name="connectionStrings">The connection strings to overwrite the ones in the Web.config file.</param>
1178        privat

Large files files are truncated, but you can click here to view the full file