diff --git a/metadata/environment.go b/metadata/environment.go
index 2bf4b6fd01bc5ea0a68c295892661eaae6c835fd..9ce084c0ec6bd46b68a0fa24ec7bf1ad1b35d383 100644
--- a/metadata/environment.go
+++ b/metadata/environment.go
@@ -29,6 +29,7 @@ import (
 
 	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet"
 	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec"
+	"github.com/ksonnet/ksonnet/metadata/snippet"
 )
 
 const (
@@ -342,6 +343,36 @@ func (m *manager) SetEnvironment(name string, desired *Environment) error {
 	return nil
 }
 
+func (m *manager) SetEnvironmentParams(env, component string, params map[string]string) error {
+	exists, err := m.environmentExists(env)
+	if err != nil {
+		return err
+	}
+	if !exists {
+		return fmt.Errorf("Environment '%s' does not exist", env)
+	}
+
+	path := appendToAbsPath(m.environmentsPath, env, paramsFileName)
+
+	text, err := afero.ReadFile(m.appFS, string(path))
+	if err != nil {
+		return err
+	}
+
+	appended, err := snippet.SetEnvironmentParams(component, string(text), params)
+	if err != nil {
+		return err
+	}
+
+	err = afero.WriteFile(m.appFS, string(path), []byte(appended), defaultFilePermissions)
+	if err != nil {
+		return err
+	}
+
+	log.Debugf("Successfully set parameters for component '%s' at environment '%s'", component, env)
+	return nil
+}
+
 func (m *manager) generateKsonnetLibData(spec ClusterSpec) ([]byte, []byte, []byte, error) {
 	// Get cluster specification data, possibly from the network.
 	text, err := spec.data()
diff --git a/metadata/interface.go b/metadata/interface.go
index d4f5495b16a3bd6e47c098ccd0eb5d23b7d6097a..8aa52749f7be7504c869e5b4c97c5b473d285173 100644
--- a/metadata/interface.go
+++ b/metadata/interface.go
@@ -51,6 +51,7 @@ type Manager interface {
 	GetEnvironments() ([]*Environment, error)
 	GetEnvironment(name string) (*Environment, error)
 	SetEnvironment(name string, desired *Environment) error
+	SetEnvironmentParams(env, component string, params map[string]string) error
 
 	//
 	// TODO: Fill in methods as we need them.
@@ -58,7 +59,6 @@ type Manager interface {
 	// GetPrototype(id string) Protoype
 	// SearchPrototypes(query string) []Protoype
 	// VendorLibrary(uri, version string) error
-	// SetEnvironmentParams(component, env string, params map[string]string) error
 	// GetEnvironmentParams(env string) (map[string]map[string]string, error)
 }
 
diff --git a/metadata/snippet/interface.go b/metadata/snippet/interface.go
index 7d6175cc8d33148502f7b5abe5759d8b8dea8ce7..0bff1d6ad218fca7793330eb43ea6c848c7f09a5 100644
--- a/metadata/snippet/interface.go
+++ b/metadata/snippet/interface.go
@@ -49,3 +49,14 @@ func GetComponentParams(component, snippet string) (map[string]string, error) {
 func SetComponentParams(component, snippet string, params map[string]string) (string, error) {
 	return setComponentParams(component, snippet, params)
 }
+
+// SetEnvironmentParams takes
+//
+//   component: the name of the new component to be modified.
+//   snippet: a jsonnet snippet resembling the current environment parameters (not expanded).
+//   params: the parameters to be set for 'component'.
+//
+// and returns the jsonnet snippet with the modified set of environment parameters.
+func SetEnvironmentParams(component, snippet string, params map[string]string) (string, error) {
+	return setEnvironmentParams(component, snippet, params)
+}
diff --git a/metadata/snippet/params.go b/metadata/snippet/params.go
index c63917eb83d2eb32916738d93b85e96f952cbf35..94e530f7f85d2741179eaad2f3efa85ed4842408 100644
--- a/metadata/snippet/params.go
+++ b/metadata/snippet/params.go
@@ -30,30 +30,16 @@ const (
 	componentsID = "components"
 )
 
-func visitComponentsObj(component, snippet string) (*ast.Node, error) {
+func astRoot(component, snippet string) (ast.Node, error) {
 	tokens, err := parser.Lex(component, snippet)
 	if err != nil {
 		return nil, err
 	}
 
-	root, err := parser.Parse(tokens)
-	if err != nil {
-		return nil, err
-	}
-
-	switch n := root.(type) {
-	case *ast.Object:
-		for _, field := range n.Fields {
-			if field.Id != nil && *field.Id == componentsID {
-				return &field.Expr2, nil
-			}
-		}
-	}
-	// If this point has been reached, it means we weren't able to find a top-level components object.
-	return nil, fmt.Errorf("Invalid format; expected to find a top-level components object")
+	return parser.Parse(tokens)
 }
 
-func visitComponentParams(component ast.Node) (map[string]string, *ast.LocationRange, error) {
+func visitParams(component ast.Node) (map[string]string, *ast.LocationRange, error) {
 	params := make(map[string]string)
 	var loc *ast.LocationRange
 
@@ -88,11 +74,11 @@ func visitParamValue(param ast.Node) (string, error) {
 	case *ast.LiteralString:
 		return fmt.Sprintf(`"%s"`, n.Value), nil
 	default:
-		return "", fmt.Errorf("Found an unsupported param value type: %T", n)
+		return "", fmt.Errorf("Found an unsupported param AST node type: %T", n)
 	}
 }
 
-func writeParams(params map[string]string) string {
+func writeParams(indent int, params map[string]string) string {
 	// keys maintains an alphabetically sorted list of the param keys
 	keys := make([]string, 0, len(params))
 	for key := range params {
@@ -100,10 +86,15 @@ func writeParams(params map[string]string) string {
 	}
 	sort.Strings(keys)
 
+	var indentBuffer bytes.Buffer
+	for i := 0; i < indent; i++ {
+		indentBuffer.WriteByte(' ')
+	}
+
 	var buffer bytes.Buffer
 	buffer.WriteString("\n")
 	for i, key := range keys {
-		buffer.WriteString(fmt.Sprintf("      %s: %s,", key, params[key]))
+		buffer.WriteString(fmt.Sprintf("%s%s: %s,", indentBuffer.String(), key, params[key]))
 		if i < len(keys)-1 {
 			buffer.WriteString("\n")
 		}
@@ -112,6 +103,27 @@ func writeParams(params map[string]string) string {
 	return buffer.String()
 }
 
+// ---------------------------------------------------------------------------
+// Component Parameter-specific functionality
+
+func visitComponentsObj(component, snippet string) (*ast.Node, error) {
+	root, err := astRoot(component, snippet)
+	if err != nil {
+		return nil, err
+	}
+
+	switch n := root.(type) {
+	case *ast.Object:
+		for _, field := range n.Fields {
+			if field.Id != nil && *field.Id == componentsID {
+				return &field.Expr2, nil
+			}
+		}
+	}
+	// If this point has been reached, it means we weren't able to find a top-level components object.
+	return nil, fmt.Errorf("Invalid format; expected to find a top-level components object")
+}
+
 func appendComponent(component, snippet string, params map[string]string) (string, error) {
 	componentsNode, err := visitComponentsObj(component, snippet)
 	if err != nil {
@@ -136,7 +148,7 @@ func appendComponent(component, snippet string, params map[string]string) (strin
 	// Create the jsonnet resembling the component params
 	var buffer bytes.Buffer
 	buffer.WriteString("    " + component + ": {")
-	buffer.WriteString(writeParams(params))
+	buffer.WriteString(writeParams(6, params))
 	buffer.WriteString("    },")
 
 	// Insert the new component to the end of the list of components
@@ -158,7 +170,7 @@ func getComponentParams(component, snippet string) (map[string]string, *ast.Loca
 	case *ast.Object:
 		for _, field := range n.Fields {
 			if field.Id != nil && string(*field.Id) == component {
-				return visitComponentParams(field.Expr2)
+				return visitParams(field.Expr2)
 			}
 		}
 	default:
@@ -182,10 +194,90 @@ func setComponentParams(component, snippet string, params map[string]string) (st
 
 	// Replace the component param fields
 	lines := strings.Split(snippet, "\n")
-	paramsSnippet := writeParams(params)
+	paramsSnippet := writeParams(6, params)
+	newSnippet := strings.Join(lines[:loc.Begin.Line], "\n") + paramsSnippet + strings.Join(lines[loc.End.Line-1:], "\n")
+
+	return newSnippet, nil
+}
+
+// ---------------------------------------------------------------------------
+// Environment Parameter-specific functionality
+
+func findEnvComponentsObj(node ast.Node) (ast.Node, error) {
+	switch n := node.(type) {
+	case *ast.Local:
+		return findEnvComponentsObj(n.Body)
+	case *ast.Binary:
+		return findEnvComponentsObj(n.Right)
+	case *ast.Object:
+		for _, f := range n.Fields {
+			if *f.Id == "components" {
+				return f.Expr2, nil
+			}
+		}
+		return nil, fmt.Errorf("Invalid params schema -- found %T that is not 'components'", n)
+	}
+	return nil, fmt.Errorf("Invalid params schema -- did not expect type: %T", node)
+}
+
+func getEnvironmentParams(component, snippet string) (map[string]string, *ast.LocationRange, bool, error) {
+	root, err := astRoot(component, snippet)
+	if err != nil {
+		return nil, nil, false, err
+	}
+
+	componentsNode, err := findEnvComponentsObj(root)
+	if err != nil {
+		return nil, nil, false, err
+	}
+
+	switch n := componentsNode.(type) {
+	case *ast.Object:
+		for _, f := range n.Fields {
+			if f.Id != nil && string(*f.Id) == component {
+				params, loc, err := visitParams(f.Expr2)
+				return params, loc, true, err
+			}
+		}
+		// If this point has been reached, it's because we don't have the
+		// component in the list of params, return the location after the
+		// last field of the components obj
+		loc := ast.LocationRange{
+			Begin: ast.Location{Line: n.Loc().End.Line - 1, Column: n.Loc().End.Column},
+			End:   ast.Location{Line: n.Loc().End.Line, Column: n.Loc().End.Column},
+		}
+
+		return make(map[string]string), &loc, false, nil
+	}
+
+	return nil, nil, false, fmt.Errorf("Could not find component identifier '%s' when attempting to set params", component)
+}
+
+func setEnvironmentParams(component, snippet string, params map[string]string) (string, error) {
+	currentParams, loc, hasComponent, err := getEnvironmentParams(component, snippet)
+	if err != nil {
+		return "", err
+	}
+
+	for k, v := range currentParams {
+		if _, ok := params[k]; !ok {
+			params[k] = v
+		}
+	}
+
+	// Replace the component param fields
+	var paramsSnippet string
+	lines := strings.Split(snippet, "\n")
+	if !hasComponent {
+		var buffer bytes.Buffer
+		buffer.WriteString(fmt.Sprintf("\n    %s +: {", component))
+		buffer.WriteString(writeParams(6, params))
+		buffer.WriteString("    },\n")
+		paramsSnippet = buffer.String()
+	} else {
+		paramsSnippet = writeParams(6, params)
+	}
 	newSnippet := strings.Join(lines[:loc.Begin.Line], "\n") + paramsSnippet + strings.Join(lines[loc.End.Line-1:], "\n")
-	//newSnippet := append(lines[:loc.Begin.Line], paramsSnippet)
-	//newSnippet = append(newSnippet, strings.Join(lines[loc.End.Line-1:], "\n"))
 
 	return newSnippet, nil
 }
diff --git a/metadata/snippet/params_test.go b/metadata/snippet/params_test.go
index 88765d70fcd7eed01cb20426fdeb39a4955b5b6d..e3a38156a1bbbed25e4938820228931f4dc29590 100644
--- a/metadata/snippet/params_test.go
+++ b/metadata/snippet/params_test.go
@@ -402,3 +402,127 @@ func TestSetComponentParams(t *testing.T) {
 		}
 	}
 }
+
+func TestSetEnvironmentParams(t *testing.T) {
+	tests := []struct {
+		componentName string
+		jsonnet       string
+		params        map[string]string
+		expected      string
+	}{
+		// Test environment param case
+		{
+			"foo",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    foo +: {
+      name: "foo",
+      replicas: 1,
+    },
+  },
+}`,
+			map[string]string{"replicas": "5"},
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    foo +: {
+      name: "foo",
+      replicas: 5,
+    },
+  },
+}`,
+		},
+		// Test environment param case with multiple components
+		{
+			"foo",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      replicas: 1,
+    },
+    foo +: {
+      name: "foo",
+      replicas: 1,
+    },
+  },
+}`,
+			map[string]string{"name": `"foobar"`, "replicas": "5"},
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      replicas: 1,
+    },
+    foo +: {
+      name: "foobar",
+      replicas: 5,
+    },
+  },
+}`,
+		},
+		// Test setting environment param case where component isn't in the snippet
+		{
+			"foo",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+  },
+}`,
+			map[string]string{"replicas": "5"},
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    foo +: {
+      replicas: 5,
+    },
+  },
+}`,
+		},
+	}
+
+	errors := []struct {
+		componentName string
+		jsonnet       string
+		params        map[string]string
+	}{
+		// Test bad schema
+		{
+			"foo",
+			`
+local params = import "/fake/path";
+params + {
+  badobj +: {
+  },
+}`,
+			map[string]string{"replicas": "5"},
+		},
+	}
+
+	for _, s := range tests {
+		parsed, err := SetEnvironmentParams(s.componentName, s.jsonnet, s.params)
+		if err != nil {
+			t.Errorf("Unexpected error\n  input: %v\n  error: %v", s.jsonnet, err)
+		}
+
+		if parsed != s.expected {
+			t.Errorf("Wrong conversion\n  expected:%v\n  got:%v", s.expected, parsed)
+		}
+	}
+
+	for _, e := range errors {
+		parsed, err := SetEnvironmentParams(e.componentName, e.jsonnet, e.params)
+		if err == nil {
+			t.Errorf("Expected error but not found\n  input: %v  got: %v", e, parsed)
+		}
+	}
+}