public void ShouldResolveASGConflicts() { var q = new EventQueue(EventQueues.AutoScalingGroupEvents); var traits = AwsSchema.GetResourceTraits("aws_autoscaling_group"); var resolver = new EventQueuePreprocessor(q); resolver.ResolveConflicts(); }
/// <summary> /// Renders the specified GetAtt intrinsic. /// </summary> /// <param name="getAttIntrinsic">The GetAtt intrinsic.</param> /// <param name="template">The template.</param> /// <param name="resource">The resource.</param> /// <returns>An <see cref="IndirectReference"/> to an attribute on another resource.</returns> private static Reference Render(GetAttIntrinsic getAttIntrinsic, ITemplate template, ResourceMapping resource) { string attributeName; if (getAttIntrinsic.AttributeName is IIntrinsic) { if (!(getAttIntrinsic.AttributeName is RefIntrinsic refIntrinsic)) { // Only !Ref allowed here return(null); } attributeName = refIntrinsic.Evaluate(template).ToString(); } else { attributeName = getAttIntrinsic.AttributeName.ToString(); } // This GetAtt refers to a nested stack which may or may not have been imported as a terraform module. if (attributeName.StartsWith(TerraformExporterConstants.StackOutputQualifier)) { var referencedOutput = attributeName.Substring(TerraformExporterConstants.StackOutputQualifier.Length); if (resource.Module?.LogicalId == getAttIntrinsic.LogicalId && resource.Module.Outputs.Any(o => o.OutputKey == referencedOutput)) { // This reference is to a module output return(new ModuleReference($"{resource.Module.Name}.{referencedOutput}")); } // The reference is to an aws_cloudformation_stack as the use did not elect to import nested stacks. // Just lowercase "Outputs." which will then resolve to the terraform resource. attributeName = attributeName.Replace( TerraformExporterConstants.StackOutputQualifier, TerraformExporterConstants.StackOutputQualifier.ToLowerInvariant()); } else { var traits = AwsSchema.GetResourceTraits(resource.TerraformType); attributeName = traits.AttributeMap.ContainsKey(attributeName) ? traits.AttributeMap[attributeName] : attributeName.CamelCaseToSnakeCase(); } if (getAttIntrinsic.ExtraData is IntrinsicInfo intrinsicInfo) { resource = intrinsicInfo.TargetResource; } return(template.Resources.Any(r => r.Name == resource.LogicalId) ? new IndirectReference($"{resource.Address}.{attributeName}") : null); }
/// <summary> /// Emits the resource start. /// </summary> /// <param name="event">A <see cref="ResourceStart"/> event.</param> private void EmitResourceStart(HclEvent @event) { var rs = GetTypedEvent <ResourceStart>(@event); this.Write("resource"); this.isWhitespace = false; this.resourceTraits = AwsSchema.GetResourceTraits(rs.ResourceType); this.currentResourceName = rs.ResourceName; this.currentResourceType = rs.ResourceType; this.EmitScalar(new Scalar(rs.ResourceType, true)); this.EmitScalar(new Scalar(rs.ResourceName, true)); }
/// <summary> /// Creates an <see cref="IntrinsicInfo"/> for a <c>!GetAtt</c> intrinsic. /// </summary> /// <param name="getAttIntrinsic">The <c>!GetAtt</c> intrinsic.</param> /// <param name="currentPath">The current path.</param> /// <returns>An <see cref="IntrinsicInfo"/></returns> private IntrinsicInfo ProcessGetAtt(GetAttIntrinsic getAttIntrinsic, PropertyPath currentPath) { object evaluation; // Logical name of the resource being referenced by this !GetAtt var(referencedResourceName, attribute) = (Tuple <string, string>)getAttIntrinsic.Evaluate(this.template); // Is the reference to a nested stack module? var referencedModule = this.module.NestedModules.FirstOrDefault(m => m.LogicalId == referencedResourceName); if (referencedModule != null) { var targetModuleSummary = new ResourceMapping { AwsType = TerraformExporterConstants.AwsCloudFormationStack, LogicalId = referencedModule.LogicalId, PhysicalId = referencedModule.Name, Module = referencedModule }; var parts = attribute.Split('.'); evaluation = referencedModule.Outputs.Where(o => o.OutputKey == parts[1]) .Select(o => o.OutputValue).SingleOrDefault(); return(new IntrinsicInfo(currentPath, getAttIntrinsic, targetModuleSummary, evaluation)); } // State file instance of the resource being referenced by this !GetAtt var referencedResource = this.TerraformResources.FirstOrDefault(r => r.Name == referencedResourceName) ?.Instances.First(); if (referencedResource == null) { // If not found, then reference is to a resource that couldn't be imported eg. a custom resource. throw new UnsupportedResourceWarning( getAttIntrinsic, this.currentCloudFormationResource, currentPath); } // CloudFormation instance of the resource being referenced by this !GetAtt var cloudFormationResource = this.CloudFormationResources.First(r => r.LogicalResourceId == getAttIntrinsic.LogicalId); // Summary of the resource to which this !GetAtt refers to var targetResourceSummary = new ResourceMapping { AwsType = cloudFormationResource.ResourceType, LogicalId = cloudFormationResource.LogicalResourceId, PhysicalId = cloudFormationResource.PhysicalResourceId, TerraformType = this.TerraformResources.First( tr => tr.Name == cloudFormationResource.LogicalResourceId).Type }; // Now attempt to match up the CloudFormation resource attribute name with the corresponding terraform one // and get the current value from state. // First, look up the attribute map var traits = AwsSchema.GetResourceTraits(referencedResource.Parent.Type); if (traits.AttributeMap.ContainsKey(attribute)) { var token = referencedResource.Attributes[traits.AttributeMap[attribute]]; evaluation = GetEvaluation(token); } else if (attribute.StartsWith(TerraformExporterConstants.StackOutputQualifier)) { // Nested stack output reference var token = referencedResource.Attributes.SelectToken( attribute.Replace( TerraformExporterConstants.StackOutputQualifier, TerraformExporterConstants.StackOutputQualifier.ToLowerInvariant())); evaluation = GetEvaluation(token); } else { var result = getAttIntrinsic.GetTargetValue(this.template, referencedResource); if (result.Success) { evaluation = result.Value; } else { throw new UnreferenceableIntrinsicWarning( getAttIntrinsic, cloudFormationResource.TemplateResource, currentPath); } } return(new IntrinsicInfo(currentPath, getAttIntrinsic, targetResourceSummary, evaluation)); object GetEvaluation(JToken token) { if (token is JValue jv) { // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault switch (jv.Type) { case JTokenType.String: evaluation = jv.Value <string>(); break; case JTokenType.Integer: case JTokenType.Float: evaluation = jv.Value <double>(); break; case JTokenType.Boolean: evaluation = jv.Value <bool>(); break; default: throw new InvalidOperationException( $"Unexpected JValue type: {jv.Type} while processing {getAttIntrinsic}"); } } else { throw new InvalidOperationException( $"Unexpected JToken type: {token.Type} while processing {getAttIntrinsic}"); } return(evaluation); } }
/// <summary> /// Initializes a new instance of the <see cref="EventQueuePreprocessor"/> class. /// </summary> /// <param name="queue">The event queue.</param> public EventQueuePreprocessor(EventQueue queue) { this.queue = queue; this.traits = AwsSchema.GetResourceTraits(this.GetResourceType()); }