示例#1
0
        /// <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);
        }
示例#2
0
        /// <summary>
        /// Gets the resolved name of the target attribute.
        /// </summary>
        /// <param name="self">Instance of intrinsic.</param>
        /// <param name="template">The template.</param>
        /// <returns>Resolved name of attribute targeted by this intrinsic.</returns>
        public static string GetResolvedAttributeName(this GetAttIntrinsic self, ITemplate template)
        {
            if (self == null)
            {
                throw new ArgumentNullException(nameof(self));
            }

            return(self.AttributeName is IIntrinsic intrinsic
                       ? intrinsic.Evaluate(template).ToString()
                       : self.AttributeName.ToString());
        }
示例#3
0
        /// <summary>
        /// Gets the value of the property on the target terraform resource referenced by this <c>!GetAtt</c>.
        /// </summary>
        /// <param name="self">Instance of intrinsic.</param>
        /// <param name="template">The template.</param>
        /// <param name="targetResource">The target resource.</param>
        /// <returns>An <see cref="IGetAttTargetEvaluation"/> containing result of the underlying resource visit.</returns>
        public static IGetAttTargetEvaluation GetTargetValue(this GetAttIntrinsic self, ITemplate template, StateFileResourceInstance targetResource)
        {
            if (self == null)
            {
                throw new ArgumentNullException(nameof(self));
            }

            var context = new TerraformAttributeGetterContext(self.GetResolvedAttributeName(template));

            targetResource.Attributes.Accept(new TerraformAttributeGetterVisitor(), context);
            return(context);
        }
示例#4
0
            /// <summary>
            /// Begin processing an intrinsic.
            /// </summary>
            /// <param name="intrinsic">The intrinsic.</param>
            /// <param name="currentPath">The current path.</param>
            public void EnterIntrinsic(IIntrinsic intrinsic, PropertyPath currentPath)
            {
                if (this.lastWarning != null)
                {
                    // Something that can't be resolved has been found,
                    // so don't process any more here.
                    return;
                }

                var clonedPath = currentPath.Clone();

                try
                {
                    if (this.parentIntrinsicPath == null)
                    {
                        // This is a "top level" intrinsic associated directly with a resource attribute.
                        if (this.currentCloudFormationResource.Type == "AWS::Lambda::Permission" &&
                            currentPath.Path == "FunctionName" && intrinsic is RefIntrinsic)
                        {
                            // !! NASTY KLUDGE ALERT !!
                            // AWS treats this property as a !Ref which is lambda function's name, but terraform actually wants the ARN here.
                            // If I find more cases like this, then I'll put something into resource traits
                            intrinsic = new GetAttIntrinsic(
                                intrinsic.GetReferencedObjects(this.template).First(),
                                "Arn");
                        }

                        this.intrinsicInfos.Push(this.currentIntrinsicInfo);
                        this.parentIntrinsicPath  = clonedPath;
                        this.currentIntrinsicInfo = this.GetIntrinsicInfo(intrinsic, currentPath);
                    }
                    else
                    {
                        // We have descended the graph to member intrinsic
                        this.intrinsicInfos.Push(this.currentIntrinsicInfo);
                        var intrinsicInfo = this.GetIntrinsicInfo(intrinsic, currentPath);
                        this.currentIntrinsicInfo.NestedIntrinsics.Add(intrinsicInfo);

                        this.currentIntrinsicInfo = intrinsicInfo;
                    }
                }
                catch (DependencyResolutionWarning w)
                {
                    this.lastWarning = w;
                }
            }
示例#5
0
            /// <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);
                }
            }