PageRenderTime 5ms CodeModel.GetById 6ms app.highlight 61ms RepoModel.GetById 0ms app.codeStats 1ms

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

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

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