func ConfigValueFromHCL2(v cty.Value) interface{} {
	if !v.IsKnown() {
		return UnknownVariableValue
	}
	if v.IsNull() {
		return nil
	}
	switch v.Type() {
	case cty.Bool:
		return v.True()  
	case cty.String:
		return v.AsString()  
	case cty.Number:
		f := v.AsBigFloat()
		if i, acc := f.Int64(); acc == big.Exact {
			const MaxInt = int(^uint(0) >> 1)
			const MinInt = -MaxInt - 1
			if i <= int64(MaxInt) && i >= int64(MinInt) {
				return int(i)  
			}
		}
		f64, _ := f.Float64()
		return f64  
	}
	if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
		l := make([]interface{}, 0, v.LengthInt())
		it := v.ElementIterator()
		for it.Next() {
			_, ev := it.Element()
			l = append(l, ConfigValueFromHCL2(ev))
		}
		return l
	}
	if v.Type().IsMapType() || v.Type().IsObjectType() {
		l := make(map[string]interface{})
		it := v.ElementIterator()
		for it.Next() {
			ek, ev := it.Element()
			cv := ConfigValueFromHCL2(ev)
			if cv != nil {
				l[ek.AsString()] = cv
			}
		}
		return l
	}
	panic(fmt.Errorf("can't convert %#v to config value", v))
}
ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic
types library that HCL2 uses) to a value type that matches what would've
been produced from the HCL-based interpolator for an equivalent structure.

This function will transform a cty null value into a Go nil value, which
isn't a possible outcome of the HCL/HIL-based decoder and so callers may
need to detect and reject any null values.
package hcl2shim
import (
	"fmt"
	"math/big"
	"github.com/zclconf/go-cty/cty"
	"github.com/camptocamp/terraboard/internal/terraform/configs/configschema"
)
const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
func ConfigValueFromHCL2Block(v cty.Value, schema *configschema.Block) map[string]interface{} {
	if v.IsNull() {
		return nil
	}
	if !v.IsKnown() {
		panic("ConfigValueFromHCL2Block used with unknown value")
	}
	if !v.Type().IsObjectType() {
		panic(fmt.Sprintf("ConfigValueFromHCL2Block used with non-object value %#v", v))
	}
	atys := v.Type().AttributeTypes()
	ret := make(map[string]interface{})
	for name := range schema.Attributes {
		if _, exists := atys[name]; !exists {
			continue
		}
		av := v.GetAttr(name)
		if av.IsNull() {
			continue
		}
		ret[name] = ConfigValueFromHCL2(av)
	}
	for name, blockS := range schema.BlockTypes {
		if _, exists := atys[name]; !exists {
			continue
		}
		bv := v.GetAttr(name)
		if !bv.IsKnown() {
			ret[name] = UnknownVariableValue
			continue
		}
		if bv.IsNull() {
			continue
		}
		switch blockS.Nesting {
		case configschema.NestingSingle, configschema.NestingGroup:
			ret[name] = ConfigValueFromHCL2Block(bv, &blockS.Block)
		case configschema.NestingList, configschema.NestingSet:
			l := bv.LengthInt()
			if l == 0 {
				continue
			}
			elems := make([]interface{}, 0, l)
			for it := bv.ElementIterator(); it.Next(); {
				_, ev := it.Element()
				if !ev.IsKnown() {
					elems = append(elems, UnknownVariableValue)
					continue
				}
				elems = append(elems, ConfigValueFromHCL2Block(ev, &blockS.Block))
			}
			ret[name] = elems
		case configschema.NestingMap:
			if bv.LengthInt() == 0 {
				continue
			}
			elems := make(map[string]interface{})
			for it := bv.ElementIterator(); it.Next(); {
				ek, ev := it.Element()
				if !ev.IsKnown() {
					elems[ek.AsString()] = UnknownVariableValue
					continue
				}
				elems[ek.AsString()] = ConfigValueFromHCL2Block(ev, &blockS.Block)
			}
			ret[name] = elems
		}
	}
	return ret
}