/// <summary> /// Initializes a new instance of the <see cref="TemplateResolver"/> class. /// </summary> /// <param name="clientFactory">The client factory.</param> /// <param name="context">The context.</param> /// <param name="templateStage">Template stage to retrieve.</param> /// <param name="stackName">Name of the stack.</param> /// <param name="usePreviousTemplate">if set to <c>true</c> [use previous template].</param> /// <param name="forceS3">If set to <c>true</c>, force template upload to S3 even if less than max size.</param> public TemplateResolver(IAwsClientFactory clientFactory, ICloudFormationContext context, TemplateStage templateStage, string stackName, bool usePreviousTemplate, bool forceS3) : base(clientFactory, context) { this.usePreviousTemplate = usePreviousTemplate; this.stackName = stackName; this.ForceS3 = forceS3; this.templateStage = templateStage; }
/// <summary> /// Initializes a new instance of the <see cref="CloudFormationBuilder"/> class. /// </summary> /// <param name="context">The context.</param> /// <param name="stackName">Name of the stack.</param> internal CloudFormationBuilder(ICloudFormationContext context, string stackName) { this._cloudFormationContext = context ?? throw new ArgumentNullException(nameof(context)); this._stackName = stackName ?? throw new ArgumentNullException(nameof(stackName)); if (this._cloudFormationContext.Logger == null) { throw new ArgumentNullException(nameof(context), "context.Logger cannot be null"); } }
/// <summary> /// Generates name of module's private bucket /// </summary> /// <param name="context">The context.</param> private void GeneratePrivateBucketName(ICloudFormationContext context) { using (var sts = this.clientFactory.CreateSTSClient()) { var account = sts.GetCallerIdentityAsync(new GetCallerIdentityRequest()).Result.Account; var bucketName = $"cf-templates-pscloudformation-{context.Region.SystemName}-{account}"; this.cloudFormationBucket = new CloudFormationBucket { BucketName = bucketName, BucketUri = new Uri($"https://{bucketName}.s3.amazonaws.com"), Initialized = false }; } }
/// <summary> /// Resolves the given artifact location (template or policy) from text input /// uploading it to S3 if the object is larger than the maximum size for /// body text supported by the CloudFormation API. /// </summary> /// <param name="context">The context for logging.</param> /// <param name="objectToResolve">The object to resolve - either plain text, a path or an S3 location</param> /// <param name="stackName">Name of the stack.</param> /// <returns> /// Result of the resolution. /// </returns> public async Task <ResolutionResult> ResolveArtifactLocationAsync( ICloudFormationContext context, string objectToResolve, string stackName) { var result = new ResolutionResult(); await this.ResolveFileAsync(objectToResolve); var fileType = this is TemplateResolver ? UploadFileType.Template : UploadFileType.Policy; if ((this.Source & InputFileSource.Oversize) != 0) { if (context.S3Util == null) { throw new StackOperationException( $"Unable to upload oversize {fileType.ToString().ToLowerInvariant()} to S3. No implementation of {typeof(IS3Util).FullName} has been provided."); } result.ArtifactUrl = (await context.S3Util.UploadOversizeArtifactToS3( stackName, this.ArtifactContent, this.InputFileName, fileType)).AbsoluteUri; this.ResetTemplateSource(result.ArtifactUrl); } else { // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (this.Source) { case InputFileSource.S3: result.ArtifactUrl = this.ArtifactUrl; break; case InputFileSource.File: case InputFileSource.String: result.ArtifactBody = this.ArtifactContent; break; } } return(result); }
/// <summary> /// Initializes a new instance of the <see cref="DefaultClientFactory"/> class. /// </summary> /// <param name="context">The context.</param> public DefaultClientFactory(ICloudFormationContext context) { this.context = context; }
/// <summary> /// Initializes a new instance of the <see cref="StackPolicyResolver"/> class. /// </summary> /// <param name="clientFactory">The client factory.</param> /// <param name="context">The context.</param> public StackPolicyResolver(IAwsClientFactory clientFactory, ICloudFormationContext context) : base(clientFactory, context) { }
/// <summary> /// Initializes a new instance of the <see cref="AbstractFileResolver"/> class. /// </summary> /// <param name="clientFactory">The client factory.</param> /// <param name="context">The context.</param> protected AbstractFileResolver(IAwsClientFactory clientFactory, ICloudFormationContext context) { this.Context = context; this.ClientFactory = clientFactory; }
/// <summary> /// Creates a builder instance for CloudFormationRunner /// </summary> /// <param name="context">The context.</param> /// <param name="stackName">Name of the stack.</param> /// <returns> /// New builder instance /// </returns> public static CloudFormationBuilder Builder(ICloudFormationContext context, string stackName) { return(new CloudFormationBuilder(context, stackName)); }
/// <summary>Initializes a new instance of the <see cref="CloudFormationRunner"/> class.</summary> /// <param name="clientFactory">Factory for creating AWS service clients</param> /// <param name="stackName">Name of the stack.</param> /// <param name="templateLocation">Template location. Either path or URL.</param> /// <param name="parameters">The parameters.</param> /// <param name="capabilities">The capabilities.</param> /// <param name="context">The Cake context.</param> /// <param name="tags">Stack level tags.</param> /// <param name="followOperation">if set to <c>true</c> [follow operation].</param> /// <param name="waitForInProgressUpdate">if set to <c>true</c> [wait for in progress update].</param> /// <param name="deleteNoopChangeSet">if set to <c>true</c> [delete no-op change set].</param> /// <param name="changesetOnly">if set to <c>true</c> only create change set without updating.</param> /// <param name="resourcesToImportLocation">Resources to import</param> /// <param name="roleArn">Role to assume</param> /// <param name="clientToken">Client token</param> /// <param name="notificationARNs">SNS notification ARNs</param> /// <param name="usePreviousTemplate">Whether to use existing template for update.</param> /// <param name="rollbackConfiguration">The rollback configuration</param> /// <param name="stackPolicyLocation">Location of structure containing a new stack policy body.</param> /// <param name="stackPolicyDuringUpdateLocation">Location of structure containing the temporary overriding stack policy body</param> /// <param name="resourceType">The template resource types that you have permissions to work with for this create stack action.</param> /// <param name="terminationProtection">Whether to enable termination protection on the specified stack.</param> /// <param name="onFailure">Determines what action will be taken if stack creation fails.</param> /// <param name="timeoutInMinutes">The amount of time that can pass before the stack status becomes CREATE_FAILED</param> /// <param name="disableRollback">Set to <c>true</c> to disable rollback of the stack if stack creation failed.</param> /// <param name="retainResource">For stacks in the DELETE_FAILED state, a list of resource logical IDs that are associated with the resources you want to retain.</param> /// <param name="forceS3">If <c>true</c> always upload local templates to S3.</param> /// <param name="includeNestedStacks">Creates a change set for the all nested stacks specified in the template. The default behavior of this action is set to <c>false</c>. To include nested sets in a change set, specify <c>true</c>.</param> /// <remarks>Constructor is private as this class implements the builder pattern. See CloudFormation.Runner.Builder.cs</remarks> internal CloudFormationRunner( IAwsClientFactory clientFactory, string stackName, string templateLocation, IDictionary <string, string> parameters, IEnumerable <Capability> capabilities, ICloudFormationContext context, List <Tag> tags, bool followOperation, bool waitForInProgressUpdate, bool deleteNoopChangeSet, bool changesetOnly, string resourcesToImportLocation, string roleArn, string clientToken, // ReSharper disable once InconsistentNaming List <string> notificationARNs, bool usePreviousTemplate, RollbackConfiguration rollbackConfiguration, string stackPolicyLocation, string stackPolicyDuringUpdateLocation, List <string> resourceType, bool terminationProtection, OnFailure onFailure, int timeoutInMinutes, bool disableRollback, List <string> retainResource, bool forceS3, bool includeNestedStacks) { this.includeNestedStacks = includeNestedStacks; this.forceS3 = forceS3; this.retainResource = retainResource; this.disableRollback = disableRollback; this.timeoutInMinutes = timeoutInMinutes; this.onFailure = onFailure; this.terminationProtection = terminationProtection; this.resourceType = resourceType; this.stackPolicyDuringUpdateLocation = stackPolicyDuringUpdateLocation; this.stackPolicyLocation = stackPolicyLocation; this.rollbackConfiguration = rollbackConfiguration; this.usePreviousTemplate = usePreviousTemplate; this.notificationARNs = notificationARNs; this.clientToken = clientToken; this.roleArn = roleArn; this.resourcesToImportLocation = resourcesToImportLocation; this.clientFactory = clientFactory; this.context = context; this.changesetOnly = changesetOnly; this.templateLocation = templateLocation; this.stackName = stackName ?? throw new ArgumentNullException(nameof(stackName)); this.followOperation = followOperation; this.waitForInProgressUpdate = waitForInProgressUpdate; this.deleteNoopChangeSet = deleteNoopChangeSet; // Cheeky unit test detection if (context.GetType().FullName == "Castle.Proxies.ICloudFormationContextProxy") { // Don't hang around in the wait loop this.waitPollTime = 50; } if (capabilities != null) { this.capabilities = capabilities; } if (parameters != null) { this.parameters = parameters; } if (tags != null) { this.tags = tags; } this.client = this.clientFactory.CreateCloudFormationClient(); this.stackOperations = new CloudFormationOperations(this.clientFactory, this.context); // Get parameters and description from supplied template if any this.templateResolver = new TemplateResolver(this.clientFactory, this.context, this.stackName, this.usePreviousTemplate, this.forceS3); this.templateResolver.ResolveArtifactLocationAsync(this.context, this.templateLocation, this.stackName) .Wait(); if (this.templateResolver.Source != InputFileSource.None) { var parser = TemplateParser.Create(this.templateResolver.FileContent); this.templateDescription = parser.GetTemplateDescription(); // Adds base stack name + 10 chars to each nested stack to estimate logical resource ID of each nested stack this.context.Logger.SetStackNameColumnWidth( parser.GetNestedStackNames(this.stackName).Concat(new[] { this.stackName }) .Max(s => s.Length)); this.context.Logger.SetResourceNameColumnWidth(parser.GetLogicalResourceNames(this.stackName).Max(r => r.Length)); } }
/// <summary>Initializes a new instance of the <see cref="CloudFormationOperations"/> class.</summary> /// <param name="clientFactory">The client factory.</param> /// <param name="context">The context.</param> public CloudFormationOperations(IAwsClientFactory clientFactory, ICloudFormationContext context) { this.clientFactory = clientFactory; this.context = context; }
/// <summary> /// Gets the stack outputs /// </summary> /// <param name="context">The context.</param> /// <param name="clientFactory">The client factory.</param> /// <param name="parameterSetName">Name of the parameter set in force</param> /// <returns>Object containing outputs in selected format.</returns> internal async Task <object> GetStackOutputs( ICloudFormationContext context, IAwsClientFactory clientFactory, string parameterSetName) { var ops = new CloudFormationOperations(clientFactory, context); var stackOutputs = (await ops.GetStackAsync(this.StackName)).Outputs; if (this.AsHashTable) { return(new Hashtable(stackOutputs.ToDictionary(o => o.OutputKey, o => o.OutputValue))); } if (this.AsParameterBlock) { var parameters = new Dictionary <string, Dictionary <string, string> >(); foreach (var p in stackOutputs) { var parameterProps = new Dictionary <string, string> { { "Type", TemplateManager.GetParameterTypeFromStringValue(p.OutputValue) } }; if (!string.IsNullOrEmpty(p.Description)) { parameterProps.Add("Description", p.Description); } parameters.Add(p.OutputKey, parameterProps); } var block = new Dictionary <string, object> { { "Parameters", parameters } }; return(new SerializerBuilder().Build().Serialize(block)); } if (this.AsCrossStackReferences) { var ti = new CultureInfo("en-US"); var stackParam = string.Join( string.Empty, this.StackName.Split('-', '_').Select(s => ti.TextInfo.ToTitleCase(s))); return(new SerializerBuilder().Build().Serialize( stackOutputs.Where(o => !string.IsNullOrEmpty(o.ExportName)).Select( p => new Dictionary <string, Dictionary <string, string> > { { "Fn::ImportValue", new Dictionary <string, string> { { "Fn::Sub", p.ExportName.Replace(this.StackName, $"${{{stackParam}}}") } } } }).ToList())); } this.ThrowExecutionError($"Unsupported parameter set {this.ParameterSetName}", this, null); return(null); }
/// <summary> /// Initializes a new instance of the <see cref="TemplateResolver"/> class. /// </summary> /// <param name="clientFactory">The client factory.</param> /// <param name="context">The context.</param> /// <param name="stackName">Name of the stack.</param> /// <param name="usePreviousTemplate">if set to <c>true</c> [use previous template].</param> /// <param name="forceS3">If set to <c>true</c>, force template upload to S3 even if less than max size.</param> public TemplateResolver(IAwsClientFactory clientFactory, ICloudFormationContext context, string stackName, bool usePreviousTemplate, bool forceS3) : this(clientFactory, context, TemplateStage.Original, stackName, usePreviousTemplate, forceS3) { }
/// <summary> /// Initializes a new instance of the <see cref="TemplateResolver"/> class. /// </summary> /// <param name="context">The context.</param> /// <param name="stackName">Name of the stack.</param> /// <param name="usePreviousTemplate">if set to <c>true</c> reuse the existing template that is associated with the stack that you are updating.</param> /// <param name="forceS3">If set to <c>true</c>, force template upload to S3 even if less than max size.</param> public TemplateResolver(ICloudFormationContext context, string stackName, bool usePreviousTemplate, bool forceS3) : this(new DefaultClientFactory(context), context, TemplateStage.Original, stackName, usePreviousTemplate, forceS3) { }