code: def parse_user_data(variables, raw_user_data, blueprint_name):
      
      variable_values = {}
  
      for key, value in variables.items():
          if type(value) is CFNParameter:
              variable_values[key] = value.to_parameter_value()
          else:
              variable_values[key] = value
  
      template = string.Template(raw_user_data)
  
      res = ""
  
      try:
          res = template.substitute(variable_values)
      except ValueError as exp:
          raise InvalidUserdataPlaceholder(blueprint_name, exp.args[0])
      except KeyError as key:
          raise MissingVariable(blueprint_name, key)
  
      return res
docs: Parse the given user data and renders it as a template
  
      It supports referencing template variables to create userdata
      that's supplemented with information from the stack, as commonly
      required when creating EC2 userdata files.
  
      For example:
          Given a raw_user_data string: 'open file ${file}'
          And a variables dictionary with: {'file': 'test.txt'}
          parse_user_data would output: open file test.txt
  
      Args:
          variables (dict): variables available to the template
          raw_user_data (str): the user_data to be parsed
          blueprint_name (str): the name of the blueprint
  
      Returns:
          str: The parsed user data, with all the variables values and
               refs replaced with their resolved values.
  
      Raises:
          InvalidUserdataPlaceholder: Raised when a placeholder name in
                                      raw_user_data is not valid.
                                      E.g ${100} would raise this.
          MissingVariable: Raised when a variable is in the raw_user_data that
                           is not given in the blueprint