private async Task <R> HttpPost <R, T>(string path, T content = default(T), string queryString = "", string apiVersion = GeoMasterConstants.August2016Version, CancellationToken cancellationToken = default(CancellationToken))
        {
            var query    = SitePathUtility.CsmAnnotateQueryString(queryString, apiVersion);
            var response = new HttpResponseMessage();

            try
            {
                var uri  = path + query;
                var body = JsonConvert.SerializeObject(content);
                response = await _geoMasterClient.Client.PostAsync(uri, new StringContent(body, Encoding.UTF8, "application/json"), cancellationToken);

                response.EnsureSuccessStatusCode();
            }
            catch (TaskCanceledException)
            {
                if (cancellationToken != default(CancellationToken))
                {
                    throw new DataSourceCancelledException();
                }
                //if any task cancelled without provided cancellation token - we want capture exception in datasourcemanager
                throw;
            }

            if (typeof(R) == typeof(string))
            {
                return((await response.Content.ReadAsStringAsync()).CastTo <R>());
            }

            string responseContent = await response.Content.ReadAsStringAsync();

            R value = JsonConvert.DeserializeObject <R>(responseContent);

            return(value);
        }
        /// <summary>
        /// Gets the container logs for a site as a string
        /// </summary>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        ///
        /// <example>
        /// The below example shows how you call <see cref="GetLinuxContainerLogs"/> to get container logs for this app.
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///     var slot = cxt.Resource.Slot;
        ///
        ///     string containerLogs = await dp.GeoMaster.GetLinuxContainerLogs(subId, rg, name,slot);
        ///
        ///     // do any processing on the string variable containerLogs
        /// }
        /// </code>
        /// </example>
        /// <returns></returns>
        public async Task <string> GetLinuxContainerLogs(string subscriptionId, string resourceGroupName, string name, string slotName)
        {
            string path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName)}/containerlogs";
            var    geoMasterResponse = await HttpPost <string, string>(path);

            return(geoMasterResponse);
        }
        private async Task <R> PerformHttpRequest <R>(HttpMethod method, string path, string queryString, string content, string apiVersion, CancellationToken cancellationToken)
        {
            var query = SitePathUtility.CsmAnnotateQueryString(queryString, apiVersion);
            var uri   = new Uri(_geoMasterClient.BaseUri, path + query);
            HttpResponseMessage response = null;

            try
            {
                using (var request = new HttpRequestMessage(method, uri))
                {
                    try
                    {
                        if (method == HttpMethod.Post || method == HttpMethod.Put)
                        {
                            request.Content = new StringContent(content, Encoding.UTF8, "application/json");
                        }

                        var token = _geoMasterClient.AuthenticationToken;
                        if (!string.IsNullOrWhiteSpace(token))
                        {
                            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
                        }
                        response = await _geoMasterClient.Client.SendAsync(request, cancellationToken).ConfigureAwait(false);

                        response.EnsureSuccessStatusCode();
                    }
                    catch (TaskCanceledException)
                    {
                        if (cancellationToken != default(CancellationToken))
                        {
                            throw new DataSourceCancelledException();
                        }
                        //if any task cancelled without provided cancellation token - we want capture exception in datasourcemanager
                        throw;
                    }
                }

                R value;

                if (typeof(R) == typeof(string))
                {
                    value = (await response.Content.ReadAsStringAsync().ConfigureAwait(false)).CastTo <R>();
                }
                else
                {
                    string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    value = JsonConvert.DeserializeObject <R>(responseContent);
                }
                return(value);
            }
            finally
            {
                if (response != null)
                {
                    response.Dispose();
                }
            }
        }
        /// <summary>
        /// Using this method you can invoke any API on a SiteExtension that is installed on the Web App
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">The resource name</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <param name="extension">Full path to the SiteExtension and any api under that extension</param>
        /// <param name="apiVersion">(Optional Parameter) Pass an API version if required, 2016-08-01 is the default value</param>
        /// <param name="cancellationToken">(Optional Parameter) Cancellation token </param>
        /// <example>
        /// This sample shows how to call the <see cref="InvokeSiteExtension"/> method in a detector
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var resp = await dp.GeoMaster.InvokeSiteExtension<![CDATA[<dynamic>]]>(cxt.Resource.SubscriptionId,
        ///                     cxt.Resource.ResourceGroup,
        ///                     cxt.Resource.Name,
        ///                     "loganalyzer/log/eventlogs");
        ///
        ///     // do something with the response object
        ///     var responseFromSiteExtension = resp.ToString();
        ///     return res;
        /// }
        /// </code>
        /// </example>
        /// <returns></returns>
        private Task <T> InvokeSiteExtension <T>(string subscriptionId, string resourceGroupName, string name, string slotName, string extension, string apiVersion = GeoMasterConstants.August2016Version, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrWhiteSpace(extension))
            {
                throw new ArgumentNullException(nameof(extension));
            }

            string path = SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName) + SiteExtensionResource.Replace("{*extensionApiMethod}", extension, StringComparison.CurrentCultureIgnoreCase);

            return(HttpGet <T>(path, string.Empty, apiVersion, cancellationToken));
        }
Пример #5
0
        /// <summary>
        /// Using this method you can invoke any API on a SiteExtension that is installed on the Web App
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">The resource name</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <param name="extension">Full path to the SiteExtension and any api under that extension</param>
        /// <param name="apiVersion">(Optional Parameter) Pass an API version if required, 2016-08-01 is the default value</param>
        /// <param name="cancellationToken">(Optional Parameter) Cancellation token </param>
        /// <example>
        /// This sample shows how to call the <see cref="InvokeSiteExtension"/> method in a detector
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var resp = await dp.GeoMaster.InvokeSiteExtension<![CDATA[<dynamic>]]>(cxt.Resource.SubscriptionId,
        ///                     cxt.Resource.ResourceGroup,
        ///                     cxt.Resource.Name,
        ///                     "loganalyzer/log/eventlogs");
        ///
        ///     // do something with the response object
        ///     var responseFromSiteExtension = resp.ToString();
        ///     return res;
        /// }
        /// </code>
        /// </example>
        /// <returns></returns>
        private async Task <T> InvokeSiteExtension <T>(string subscriptionId, string resourceGroupName, string name, string slotName, string extension, string apiVersion = GeoMasterConstants.August2016Version, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrWhiteSpace(extension))
            {
                throw new ArgumentNullException("extension");
            }

            string path   = SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName) + SiteExtensionResource.Replace("{*extensionApiMethod}", extension);
            var    result = await HttpGet <T>(path, string.Empty, apiVersion, cancellationToken);

            return(result);
        }
        /// <summary>
        /// Gets a dictionary of all the deployments that were triggered for this Web App. The key of this dictionary
        /// is the DeploymentId
        /// </summary>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <returns></returns>
        /// <example>
        /// The below example shows how you call <see cref="GetAppDeployments"/> to find out details about the deployments that were triggered for this App.
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///     var slot = cxt.Resource.Slot;
        ///
        ///     var deployments = await dp.GeoMaster.GetAppDeployments(subId, rg, name, slot);
        ///     foreach(var deployment in deployments)
        ///     {
        ///         string deploymentId = deployment["id"].ToString();
        ///         var message = "DeploymentId = " +  deploymentId;
        ///
        ///         // get a specific property like this (Hint - View all properties
        ///         // at https://resources.azure.com)
        ///         string deployer = deployment["deployer"].ToString();
        ///
        ///         // or just loop through all the keys
        ///         foreach(string key in deployment.Keys)
        ///         {
        ///             var deploymentinfo = $" {key} = {deployment[key]}";
        ///         }
        ///     }
        /// }
        /// </code>
        /// </example>
        public async Task <List <IDictionary <string, dynamic> > > GetAppDeployments(string subscriptionId, string resourceGroupName, string name, string slotName)
        {
            string path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName)}/deployments";
            GeoMasterResponseArray geoMasterResponse = null;

            geoMasterResponse = await HttpGet <GeoMasterResponseArray>(path);

            var deployments = new List <IDictionary <string, dynamic> > ();

            foreach (var deployment in geoMasterResponse.Value)
            {
                deployments.Add(deployment.Properties);
            }
            return(deployments);
        }
        /// <summary>
        /// All the ARM or GeoMaster operations that are allowed over HTTP GET can be called via this method by passing the path. To get a list of all the HTTP GET based ARM operations, check out a WebApp on https://resources.azure.com .
        /// <para>It should be noted that the response of the ARM operation is of 3 types:</para>
        /// <para>1) Response contains a Properties{} object.</para>
        /// <para>2) Reponse contains a Value[] array which has a properties object.</para>
        /// <para>3) Response contains a Value[] array that has no properties object. </para>
        ///
        /// To invoke the right route, pass the right class to the method call i.e. GeoMasterResponse or GeoMasterResponseArray or GeoMasterResponseDynamicArray
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <param name="path">The path to the API route (for e.g. usages, recommendations)</param>
        /// <example>
        /// The below shows how to make this method call to invoke the different types of operations
        /// <code>
        ///  public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///     var slot = cxt.Resource.Slot;
        ///
        ///     var resp = await dp.GeoMaster.MakeHttpGetRequest<![CDATA[<GeoMasterResponse>]]>(subId,
        ///                rg,
        ///                name,
        ///                slot,
        ///                "sourcecontrols/web");
        ///
        ///     var repoUrl =  resp.Properties["repoUrl"];
        ///     var branch = resp.Properties["branch"];
        ///     var provisioningState = resp.Properties["provisioningState"];
        ///
        ///     var respVal = await dp.GeoMaster.MakeHttpGetRequest<![CDATA[<GeoMasterResponseDynamicArray>]]>(subId,
        ///                   rg,
        ///                   name,
        ///                   "usages");
        ///
        ///     foreach(var val in respVal.Value)
        ///     {
        ///         var unit = val.unit;
        ///         var name = val.name.value;
        ///         var localizedValue = val.name.localizedValue;
        ///         var currentValue = val.currentValue;
        ///     }
        ///
        ///     // To get properties on the site root path, just call the method like this
        ///     var siteProperties = await dp.GeoMaster.MakeHttpGetRequest<![CDATA[<GeoMasterResponse>]]>(subId,
        ///                rg,
        ///                name,
        ///                slot);
        ///     foreach(var item in siteProperties.Properties)
        ///     {
        ///         string str = item.Key + " " + item.Value;
        ///     }
        /// }
        /// </code>
        /// </example>
        public Task <T> MakeHttpGetRequest <T>(string subscriptionId, string resourceGroupName, string name, string slotName, string path = "")
        {
            if (!string.IsNullOrWhiteSpace(path))
            {
                path = path.StartsWith("/") ? path.Substring(1) : path;
            }
            if (string.IsNullOrWhiteSpace(path))
            {
                path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name)}";
            }
            else
            {
                path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name)}/{path}";
            }

            return(HttpGet <T>(path));
        }
        /// <summary>
        /// Using this method you can invoke an API on the DaaS SiteExtension that is installed on the Web App
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">The resource name</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <param name="daasApiPath">Full path to the DAAS API </param>
        /// <param name="apiVersion">(Optional Parameter) Pass an API version if required, 2016-08-01 is the default value</param>
        /// <param name="cancellationToken">(Optional Parameter) Cancellation token </param>
        /// <example>
        /// This sample shows how to call the <see cref="InvokeDaasExtension"/> method in a detector
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var resp = await dp.GeoMaster.InvokeDaasExtension<![CDATA[<dynamic>]]>(cxt.Resource.SubscriptionId,
        ///                     cxt.Resource.ResourceGroup,
        ///                     cxt.Resource.Name,
        ///                     cxt.Resource.Slot,
        ///                     "api/diagnosers");
        ///
        ///     // do something with the response object
        ///     var responseFromDaaS = resp.ToString();
        ///     return res;
        /// }
        /// </code>
        /// </example>
        /// <returns></returns>
        public Task <T> InvokeDaasExtension <T>(string subscriptionId, string resourceGroupName, string name, string slotName, string daasApiPath, string apiVersion = GeoMasterConstants.August2016Version, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrWhiteSpace(daasApiPath))
            {
                throw new ArgumentNullException("daasApiPath");
            }

            if (daasApiPath.StartsWith("api/databasetest", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("Accessing DatabaseTestController under DAAS is not allowed as it contains PII");
            }

            string extensionPath = $"daas/{daasApiPath}";

            string path = SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName) + SiteExtensionResource.Replace("{*extensionApiMethod}", extensionPath);

            return(HttpGet <T>(path, string.Empty, apiVersion, cancellationToken));
        }
        /// <summary>
        /// All the ARM or GeoMaster operations that are allowed over HTTP GET can be called via this method by passing the path.
        /// To get a list of all the HTTP GET based ARM operations, check out a WebApp on https://resources.azure.com
        /// It should be noted that the response of the ARM operation is of 3 types
        /// 1) Response contains a Properties{} object.
        /// 2) Reponse contains a Value[] array which has a properties object.
        /// 3) Response contains a Value[] array that has no properties object.
        ///
        /// To invoke the right route, pass the right class to the method call i.e. GeoMasterResponse or GeoMasterResponseArray or GeoMasterResponseDynamicArray
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="path">The path to the API route (for e.g. usages, recommendations)</param>
        /// <example>
        /// The below shows how to make this method call to invoke the different types of operations
        /// <code>
        ///  public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext cxt, Response res)
        /// {
        ///
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///
        ///     var resp = await dp.GeoMaster.MakeHttpGetRequest<![CDATA[<GeoMasterResponse>]]>(subId,
        ///                rg,
        ///                name,
        ///                "sourcecontrols/web");
        ///
        ///     var repoUrl =  resp.Properties["repoUrl"];
        ///     var branch = resp.Properties["branch"];
        ///     var provisioningState = resp.Properties["provisioningState"];
        ///
        ///     var respVal = await dp.GeoMaster.MakeHttpGetRequest<![CDATA[<GeoMasterResponseDynamicArray>]]>(subId,
        ///                   rg,
        ///                   name,
        ///                   "usages");
        ///
        ///     foreach(var val in respVal.Value)
        ///     {
        ///         var unit = val.unit;
        ///         var name = val.name.value;
        ///         var localizedValue = val.name.localizedValue;
        ///         var currentValue = val.currentValue;
        ///     }
        ///
        ///     // To get properties on the site root path, just call the method like this
        ///     var siteProperties = await dp.GeoMaster.MakeHttpGetRequest<![CDATA[<GeoMasterResponse>]]>(subId,
        ///                rg,
        ///                name);
        ///     foreach(var item in siteProperties.Properties)
        ///     {
        ///         string str = item.Key + " " + item.Value;
        ///     }
        /// }
        /// </code>
        /// </example>
        public async Task <T> MakeHttpGetRequest <T>(string subscriptionId, string resourceGroupName, string name, string path = "")
        {
            if (!string.IsNullOrWhiteSpace(path))
            {
                path = path.StartsWith("/") ? path.Substring(1) : path;
            }
            if (string.IsNullOrWhiteSpace(path))
            {
                path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name)}";
            }
            else
            {
                path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name)}/{path}";
            }

            var geoMasterResponse = await HttpGet <T>(path);

            return(geoMasterResponse);
        }
        /// <summary>
        /// Gets all the APP SETTINGS for the Web App that start with WEBSITE_ filtering out
        /// the sensitive settings like connectionstrings, tokens, secrets, keys, content shares etc.
        /// </summary>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <returns>A dictionary of AppSetting Keys and values</returns>
        /// <example>
        /// <code>
        /// This sample shows how to call the <see cref="GetAppSettings"/> method in a detector
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///     var slot = cxt.Resource.Slot;
        ///
        ///     var appSettings = await dp.GeoMaster.GetAppSettings(subId, rg, name, slot);
        ///     foreach(var key in appSettings.Keys)
        ///     {
        ///         // do something with the appSettingValue
        ///         string appSettingValue = appSettings[key];
        ///     }
        /// }
        /// </code>
        /// </example>
        public async Task <IDictionary <string, string> > GetAppSettings(string subscriptionId, string resourceGroupName, string name, string slotName)
        {
            string path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName)}/config/appsettings/list";
            var    geoMasterResponse = await HttpPost <GeoMasterResponse, string>(path);

            var properties = geoMasterResponse.Properties;
            Dictionary <string, string> appSettings = new Dictionary <string, string>();

            foreach (var item in properties)
            {
                if (item.Key.StartsWith("WEBSITE_"))
                {
                    if (!SensitiveAppSettingsEndingWith.Any(x => item.Key.EndsWith(x)))
                    {
                        appSettings.Add(item.Key, item.Value);
                    }
                }
            }
            return(appSettings);
        }
        /// <summary>
        /// Gets all App Settings that are marked sticky to the slot for this site\slot
        /// </summary>
        /// <param name="subscriptionId"></param>
        /// <param name="resourceGroupName"></param>
        /// <param name="name"></param>
        /// <returns>A dictionary of Settings that are marked sticky to the slot</returns>
        /// <example>
        /// This sample shows how to call the <see cref="GetStickySlotSettingNames"/> method in a detector
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///
        ///     var stickySettings = await dp.GeoMaster.GetStickySlotSettingNames(subId, rg, name);
        ///     foreach(var key in stickySettings.Keys)
        ///     {
        ///         // do something with the stickyslot value
        ///         string[] settings = stickySettings[key];
        ///     }
        /// }
        /// </code>
        /// </example>
        public async Task <IDictionary <string, string[]> > GetStickySlotSettingNames(string subscriptionId, string resourceGroupName, string name)
        {
            string path = SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name);

            path = path + "/config/slotConfigNames";
            GeoMasterResponse geoMasterResponse = null;

            geoMasterResponse = await HttpGet <GeoMasterResponse>(path);

            Dictionary <string, string[]> stickyToSlotSettings = new Dictionary <string, string[]>();

            foreach (var item in geoMasterResponse.Properties)
            {
                var val = new string[0];
                if (item.Value != null && item.Value is Newtonsoft.Json.Linq.JArray)
                {
                    var jArray = (Newtonsoft.Json.Linq.JArray)item.Value;
                    val = jArray.ToObject <string[]>();
                }
                stickyToSlotSettings.Add(item.Key, val);
            }
            return(stickyToSlotSettings);
        }
        /// <summary>
        /// Gets all the APP SETTINGS for the Web App that start with prefixes like WEBSITE_, FUNCTION_ etc, filtering out
        /// the sensitive settings like connectionstrings, tokens, secrets, keys, content shares etc.
        /// </summary>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        /// <returns>A dictionary of AppSetting Keys and values</returns>
        /// <example>
        /// <code>
        /// This sample shows how to call the <see cref="GetAppSettings"/> method in a detector
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///     var slot = cxt.Resource.Slot;
        ///
        ///     var appSettings = await dp.GeoMaster.GetAppSettings(subId, rg, name, slot);
        ///     foreach(var key in appSettings.Keys)
        ///     {
        ///         // do something with the appSettingValue
        ///         string appSettingValue = appSettings[key];
        ///     }
        /// }
        /// </code>
        /// </example>
        public async Task <IDictionary <string, string> > GetAppSettings(string subscriptionId, string resourceGroupName, string name, string slotName)
        {
            string path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName)}/config/appsettings/list";
            var    geoMasterResponse = await HttpPost <GeoMasterResponse, string>(path);

            var properties = geoMasterResponse.Properties;
            Dictionary <string, string> appSettings = new Dictionary <string, string>();

            foreach (var item in properties)
            {
                if (AllowedlistAppSettingsStartingWith.Any(x => item.Key.StartsWith(x)) && !SensitiveAppSettingsEndingWith.Any(x => item.Key.EndsWith(x)) ||
                    RegexMatchingPatterns.Any(x => (Regex.Match(item.Key, x).Success)))
                {
                    string value = RemovePIIFromSettings(item.Value);
                    appSettings.Add(item.Key, value);
                }
                else
                {
                    appSettings.Add(item.Key, "******");
                }
            }

            return(appSettings);
        }
        /// <summary>
        /// Gets the container logs for a site as a string
        /// </summary>
        /// <param name="subscriptionId">Subscription Id for the resource</param>
        /// <param name="resourceGroupName">The resource group that the resource is part of </param>
        /// <param name="name">Name of the resource</param>
        /// <param name="slotName">slot name (if querying for a slot, defaults to production slot)</param>
        ///
        /// <example>
        /// The below example shows how you call <see cref="GetLinuxContainerLogs"/> to get container logs for this app.
        /// <code>
        /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext<![CDATA[<App>]]> cxt, Response res)
        /// {
        ///     var subId = cxt.Resource.SubscriptionId;
        ///     var rg = cxt.Resource.ResourceGroup;
        ///     var name = cxt.Resource.Name;
        ///     var slot = cxt.Resource.Slot;
        ///
        ///     string containerLogs = await dp.GeoMaster.GetLinuxContainerLogs(subId, rg, name,slot);
        ///
        ///     // do any processing on the string variable containerLogs
        /// }
        /// </code>
        /// </example>
        /// <returns></returns>
        public Task <string> GetLinuxContainerLogs(string subscriptionId, string resourceGroupName, string name, string slotName)
        {
            string path = $"{SitePathUtility.GetSitePath(subscriptionId, resourceGroupName, name, slotName)}/containerlogs";

            return(HttpPost <string, string>(path));
        }