def _prepare_resource_block(resource: DictNode, conf: Optional[DictNode]) -> Tuple[Dict[str, Dict[str, Any]], bool]: """hclify resource if pre-conditions met. :param resource: tf planned_values resource block :param conf: tf configuration resource block :returns: - resource_block: a list of strings representing the header columns - prepared: whether conditions met to prepare data """ resource_block: Dict[str, Dict[str, Any]] = {} resource_block[resource["type"]] = {} prepared = False mode = "" if "mode" in resource: mode = resource.get("mode") # Rare cases where data block appears in resources with same name as resource block and only partial values # and where *_module resources don't have values field if mode == "managed" and "values" in resource: expressions = conf.get("expressions") if conf else None resource_block[resource["type"]][resource.get("name", "default")] = _hclify(resource["values"], expressions) resource_block[resource["type"]][resource.get("name", "default")]["__address__"] = resource.get("address") prepared = True return resource_block, prepared
def extract_resource_attributes(resource: DictNode) -> DictNode: resource_type = resource.get("Type") attributes = resource.get("Properties", {}) attributes["resource_type"] = resource_type attributes["__startline__"] = resource["__startline__"] attributes["__endline__"] = resource["__endline__"] attributes.start_mark = resource.start_mark attributes.end_mark = attributes.end_mark return attributes
def scan_resource_conf(self, conf: DictNode) -> CheckResult: # IAM authentication is only supported for MySQL and PostgreSQL engine = conf.get("Properties", {}).get("Engine", {}) if engine not in ("mysql", "postgres"): return CheckResult.UNKNOWN return super().scan_resource_conf(conf)
def scan_resource_conf(self, conf: DictNode) -> CheckResult: params = conf.get("Properties", {}).get("Parameters", {}) if params.get("audit_logs") == "enabled": return CheckResult.PASSED return CheckResult.FAILED
def scan_resource_conf(self, conf: DictNode) -> CheckResult: params = conf.get("Properties", {}).get("Parameters", {}) for param in params: if param.get("ParameterName") == "require_ssl" and param.get("ParameterValue") == "true": return CheckResult.PASSED return CheckResult.FAILED
def scan_resource_conf(self, conf: DictNode) -> CheckResult: aws_kms_alias = "aws/" properties = conf.get("Properties") if properties: kms_key_id = properties.get("KmsKeyId") if kms_key_id and aws_kms_alias not in kms_key_id: return CheckResult.PASSED return CheckResult.FAILED
def scan_resource_conf(self, conf: DictNode) -> CheckResult: log_types = ["audit"] logs_exports = conf.get("Properties", {}).get("EnableCloudwatchLogsExports", []) if all(elem in logs_exports for elem in log_types): return CheckResult.PASSED return CheckResult.FAILED
def _hclify(obj: DictNode, conf: Optional[DictNode] = None, parent_key: Optional[str] = None) -> Dict[str, List[Any]]: ret_dict = {} if not isinstance(obj, dict): raise Exception("this method receives only dicts") if hasattr(obj, "start_mark") and hasattr(obj, "end_mark"): obj["start_line"] = obj.start_mark.line obj["end_line"] = obj.end_mark.line for key, value in obj.items(): if _is_simple_type(value) or _is_list_of_simple_types(value): if parent_key == "tags": ret_dict[key] = value else: ret_dict[key] = [value] if _is_list_of_dicts(value): child_list = [] conf_val = conf.get(key, []) if conf else [] for internal_val, internal_conf_val in itertools.zip_longest( value, conf_val): if isinstance(internal_val, dict): child_list.append( _hclify(internal_val, internal_conf_val, parent_key=key)) if key == "tags": ret_dict[key] = [child_list] else: ret_dict[key] = child_list if isinstance(value, dict): child_dict = _hclify(value, parent_key=key) if parent_key == "tags": ret_dict[key] = child_dict else: ret_dict[key] = [child_dict] if conf and isinstance(conf, dict): for conf_key in conf.keys() - obj.keys(): ref = next((x for x in conf[conf_key].get("references", []) if not x.startswith(("var.", "local."))), None) if ref: ret_dict[conf_key] = [ref] return ret_dict
def check_duplicates(self, ordered_pairs, beg_mark, end_mark): """ Check for duplicate keys on the current level, this is not desirable because a dict does not support this. It overwrites it with the last occurance, which can give unexpected results """ mapping = DictNode({}, beg_mark, end_mark) for key, value in ordered_pairs: if not self.allow_nulls and value is None: raise NullError('"{}"'.format(key)) if key in mapping: raise DuplicateError('"{}"'.format(key)) mapping[key] = value return mapping
def extract_resource_attributes(resource: DictNode) -> DictNode: resource_type = resource.get("Type") attributes = resource.get("Properties", {}) if not isinstance(attributes, dict): attributes = DictNode({}, resource.start_mark, resource.end_mark) attributes["resource_type"] = resource_type attributes["__startline__"] = resource["__startline__"] attributes["__endline__"] = resource["__endline__"] attributes.start_mark = resource.start_mark attributes.end_mark = attributes.end_mark return attributes
def construct_yaml_map(self, node): # Check for duplicate keys on the current level, this is not desirable # because a dict does not support this. It overwrites it with the last # occurance, which can give unexpected results mapping = {} self.flatten_mapping(node) for key_node, value_node in node.value: key = self.construct_object(key_node, False) value = self.construct_object(value_node, False) if key in mapping: raise CfnParseError( self.filename, 'Duplicate resource found "{}" (line {})'.format( key, key_node.start_mark.line + 1), key_node.start_mark.line, key_node.start_mark.column, key) mapping[key] = value obj, = SafeConstructor.construct_yaml_map(self, node) return DictNode(obj, node.start_mark, node.end_mark)
def multi_constructor(loader, tag_suffix, node): """ Deal with !Ref style function format """ if tag_suffix not in UNCONVERTED_SUFFIXES: tag_suffix = '{}{}'.format(FN_PREFIX, tag_suffix) constructor = None if tag_suffix == 'Fn::GetAtt': constructor = construct_getatt elif isinstance(node, ScalarNode): constructor = loader.construct_scalar elif isinstance(node, SequenceNode): constructor = loader.construct_sequence elif isinstance(node, MappingNode): constructor = loader.construct_mapping else: raise 'Bad tag: !{}'.format(tag_suffix) return DictNode({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)