示例#1
0
        /// <summary>
        /// Starts a deployment at provided target scope and returns <see cref="BicepDeploymentStartResponse"/>.
        /// </summary>
        /// <param name="deploymentCollectionProvider">deployment collection provider</param>
        /// <param name="armClient">arm client</param>
        /// <param name="documentPath">path to bicep file used in deployment</param>
        /// <param name="template">template used in deployment</param>
        /// <param name="parametersFilePath">path to parameter file used in deployment</param>
        /// <param name="id">id string to create the ResourceIdentifier from</param>
        /// <param name="scope">target scope</param>
        /// <param name="location">location to store the deployment data</param>
        /// <param name="deploymentId">deployment id</param>
        /// <param name="parametersFileName">parameters file name</param>
        /// <param name="parametersFileUpdateOption"><see cref="ParametersFileUpdateOption"/>update, create or overwrite parameters file</param>
        /// <param name="updatedDeploymentParameters">parameters that were updated during deployment flow</param>
        /// <param name="portalUrl">azure management portal URL</param>
        /// <param name="deploymentName">deployment name</param>
        /// <param name="deploymentOperationsCache">deployment operations cache that needs to be updated</param>
        /// <returns><see cref="BicepDeploymentStartResponse"/></returns>
        public static async Task <BicepDeploymentStartResponse> StartDeploymentAsync(
            IDeploymentCollectionProvider deploymentCollectionProvider,
            ArmClient armClient,
            string documentPath,
            string template,
            string parametersFilePath,
            string id,
            string scope,
            string location,
            string deploymentId,
            string parametersFileName,
            ParametersFileUpdateOption parametersFileUpdateOption,
            List <BicepUpdatedDeploymentParameter> updatedDeploymentParameters,
            string portalUrl,
            string deploymentName,
            IDeploymentOperationsCache deploymentOperationsCache)
        {
            if ((scope == LanguageConstants.TargetScopeTypeSubscription ||
                 scope == LanguageConstants.TargetScopeTypeManagementGroup) &&
                string.IsNullOrWhiteSpace(location))
            {
                return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.MissingLocationDeploymentFailedMessage, documentPath), null));
            }

            ArmDeploymentCollection?deploymentCollection;
            var resourceIdentifier = new ResourceIdentifier(id);

            try
            {
                deploymentCollection = deploymentCollectionProvider.GetDeploymentCollection(armClient, resourceIdentifier, scope);
            }
            catch (Exception e)
            {
                return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedWithExceptionMessage, documentPath, e.Message), null));
            }

            if (deploymentCollection is not null)
            {
                JsonElement parameters;

                try
                {
                    var updatedParametersFileContents = DeploymentParametersHelper.GetUpdatedParametersFileContents(documentPath, parametersFileName, parametersFilePath, parametersFileUpdateOption, updatedDeploymentParameters);
                    parameters = JsonElementFactory.CreateElement(updatedParametersFileContents);
                }
                catch (Exception e)
                {
                    return(new BicepDeploymentStartResponse(false, e.Message, null));
                }

                var deploymentProperties = new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
                {
                    Template   = new BinaryData(JsonDocument.Parse(template).RootElement),
                    Parameters = new BinaryData(parameters)
                };
                var armDeploymentContent = new ArmDeploymentContent(deploymentProperties)
                {
                    Location = location,
                };

                try
                {
                    var deploymentOperation = await deploymentCollection.CreateOrUpdateAsync(WaitUntil.Started, deploymentName, armDeploymentContent);

                    if (deploymentOperation is null)
                    {
                        return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedMessage, documentPath), null));
                    }

                    deploymentOperationsCache.CacheDeploymentOperation(deploymentId, deploymentOperation);

                    var linkToDeploymentInAzurePortal = GetLinkToDeploymentInAzurePortal(portalUrl, id, deploymentName);

                    return(new BicepDeploymentStartResponse(
                               true,
                               string.Format(LangServerResources.DeploymentStartedMessage, documentPath),
                               string.Format(LangServerResources.ViewDeploymentInPortalMessage, linkToDeploymentInAzurePortal)));
                }
                catch (Exception e)
                {
                    return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedWithExceptionMessage, documentPath, e.Message), null));
                }
            }

            return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedMessage, documentPath), null));
        }
示例#2
0
        // Per documentation here- https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/parameter-files
        // parameters file should be of below format:
        //{
        //  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
        //  "contentVersion": "1.0.0.0",
        //  "parameters": {
        //    "<first-parameter-name>": {
        //      "value": "<first-value>"
        //    },
        //    "<second-parameter-name>": {
        //      "value": "<second-value>"
        //    }
        //  }
        //}
        // However, azure-sdk-for-net expects parameters to be of name and value pairs:
        // https://github.com/Azure/azure-sdk-for-net/blob/1e25b1bfc9b54df35d907aa7b2c10ff07082e845/sdk/resources/Azure.ResourceManager.Resources/src/Generated/Models/ArmDeploymentProperties.cs#L27
        // We'll work around the above issue by first detecting the format of the file.
        // If it's in the format descibed in the docs, we'll extract the parameters value and use that for actual deployment.
        // If the user chose to create a new parameters file during the deployment flow, we'll follow the format
        // mentioned in the docs as a best practise.
        public static string GetUpdatedParametersFileContents(
            string documentPath,
            string parametersFileName,
            string parametersFilePath,
            ParametersFileUpdateOption updateOrCreateParametersFile,
            IEnumerable <BicepUpdatedDeploymentParameter> updatedDeploymentParameters)
        {
            try
            {
                // Parameter file follows format mentioned here: https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/parameter-files
                var armSchemaStyleParametersFile = @"{
  ""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#"",
  ""contentVersion"": ""1.0.0.0"",
  ""parameters"": {
  }
}";
                // We will send across the secure param values to the azure sdk that handles deployment,
                // but will avoid writing it to the parameters file for security reasons
                var updatedParametersFile = !string.IsNullOrWhiteSpace(parametersFilePath) ?
                                            File.ReadAllText(parametersFilePath) : armSchemaStyleParametersFile;
                var updatedParametersFileWithoutSecureParams = updatedParametersFile;

                var jObject = GetParametersObjectValue(updatedParametersFile, out bool isArmStyleTemplate);

                foreach (var updatedDeploymentParameter in updatedDeploymentParameters.Reverse())
                {
                    var name = updatedDeploymentParameter.name;

                    // Check to make sure parameters mentioned in parameters file are not overwritten
                    if (jObject.ContainsKey(name))
                    {
                        continue;
                    }
                    else
                    {
                        var jsonEditor = new JsonEditor(updatedParametersFile);

                        var propertyPaths = new List <string>();
                        if (isArmStyleTemplate)
                        {
                            propertyPaths.Add("parameters");
                            propertyPaths.Add(name);
                        }
                        else
                        {
                            propertyPaths.Add(name);
                        }

                        var valueObject = UpdateJObjectBasedOnParameterType(
                            updatedDeploymentParameter.parameterType,
                            updatedDeploymentParameter.value,
                            JObject.Parse("{}"));

                        (int line, int column, string text)? insertion = jsonEditor.InsertIfNotExist(propertyPaths.ToArray(), valueObject);

                        if (insertion.HasValue)
                        {
                            var(line, column, insertText) = insertion.Value;

                            updatedParametersFile = JsonEditor.ApplyInsertion(updatedParametersFile, (line, column, insertText));

                            if (!updatedDeploymentParameter.isSecure)
                            {
                                updatedParametersFileWithoutSecureParams = JsonEditor.ApplyInsertion(updatedParametersFileWithoutSecureParams, (line, column, insertText));
                            }
                        }
                    }
                }

                if (updatedDeploymentParameters.Any())
                {
                    if (updateOrCreateParametersFile == ParametersFileUpdateOption.Update)
                    {
                        File.WriteAllText(parametersFilePath, updatedParametersFileWithoutSecureParams);
                    }
                    // ParametersFileCreateOrUpdate will have a value of "Overwrite" only if the parameters
                    // file with name <bicep_file_name>.parameters.json already exists and user chose to
                    // overwrite it with values from this deployment
                    else if (updateOrCreateParametersFile == ParametersFileUpdateOption.Create ||
                             updateOrCreateParametersFile == ParametersFileUpdateOption.Overwrite)
                    {
                        var directoryContainingBicepFile = Path.GetDirectoryName(documentPath);
                        if (directoryContainingBicepFile is not null)
                        {
                            File.WriteAllText(Path.Combine(directoryContainingBicepFile, parametersFileName), updatedParametersFileWithoutSecureParams);
                        }
                    }
                }

                var updatedJObject = GetParametersObjectValue(updatedParametersFile, out _);

                return(updatedJObject.ToString());
            }
            catch (Exception e)
            {
                throw new Exception(string.Format(LangServerResources.InvalidParameterFileDeploymentFailedMessage, documentPath, parametersFilePath, e.Message));
            }
        }