cfer icon indicating copy to clipboard operation
cfer copied to clipboard

Improve support for cross-stack references with output exports and Fn::ImportValue

Open rlister opened this issue 7 years ago • 1 comments

Cloudformation now allows cross-stack imports using. We should add support for Export in the output function, and Fn::ImportValue.

rlister avatar Mar 23 '18 20:03 rlister

Copying the conversation here, rather than the PR:

I can't accept this one as-is because it breaks the Export functionality as it works today. I know of at least two companies that are using that today, and I'm not comfortable asking them to make this change for stylistic reasons.

"publiczone": {
  "Export": {
    "Name": {
      "Name": "PublicZone"
      }
    }
  },
  "Value": {
    "Ref": "PublicZone"
  }
},

However, I do appreciate that the current way of using exports is not the most rubyish.

The way I'd prefer to accomplish this is something like this:

def output(name, value, options = {})
  self[:Outputs][name] = options.merge('Value' => value)
end

def export(name, value, options = {})
    output name, value, options.merge(Export: { 'Name' => name })
end

That way, output is always a fall-back escape-hatch.

Here is my latest technique for solving this problem. This method also includes a naming convention, based on our "Environment+Application+Facet" model for deploying the same infrastructure multiple times. Solving the "Export" noise hasn't been a high priority for me because I usually use it encapsulated by a specific pattern like this, since exports are a global namespace outside of stacks.

Cfer::Core::Stack.extend_stack do
  def exported_val_name(name, options={})
    env = options[:environment] || parameters[:environment] # parameters[:environment] is always present
    app = options[:application] || parameters[:application] # parameters[:application] is always present
    facet = options[:facet] || parameters[:facet] # parameters[:facet] is always present, and defaults to "main"
    { "Fn::Join": [options[:delim] || "-", [env, app, facet, name]] }
  end

  def opscues_import(app, rc, options={})
    val_name = exported_val_name(rc, options.merge(application: app))

    { "Fn::ImportValue": val_name }
  end

  def opscues_export(name, value, options = {})
    export_name = exported_val_name(name, options)
    output name.to_s.gsub(/[^A-Za-z0-9]+/, ''), value, Export: { Name: export_name }
  end
end

seanedwards avatar Apr 23 '18 18:04 seanedwards