diff --git a/src/client_cli/cmdTemplate/helper.go b/src/client_cli/cmdTemplate/helper.go
index e6e996116ecf218a6b4e8db7194b4e43c733773e..9fbd514eaa88b4f33fd45a6ffd6120d0ff28baf7 100644
--- a/src/client_cli/cmdTemplate/helper.go
+++ b/src/client_cli/cmdTemplate/helper.go
@@ -44,8 +44,8 @@ Regex examples:
 }
 
 // Returns a list of filtered templates for a given route.
-// The filter is based on the specified IDs and regexes.
-// The filters argument can either be:
+// Filters are based on patterns describing IDs or regexes.
+// Patterns can either be:
 // - any number of "template IDs" (UUID)
 // - any number of "template names"
 // - any combination of "template IDs" and "template names"
@@ -55,24 +55,24 @@ Regex examples:
 //   ""    -> matches everything
 //   "."   -> matches everything
 //   "bla" -> matches any template name containing "bla"
-func printFilteredTemplates(c cmd.Command, filters []string, route string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	if len(filters) < 1 {
+func printFilteredTemplates(c cmd.Command, patterns []string, route string) int {
+	if len(patterns) < 1 {
 		c.PrintUsage()
 		return 1
 	}
 
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
 	var ids []string
 	var regexes []string
 
-	for _, filter := range filters {
-		_, err := uuid.Parse(filter)
+	for _, pattern := range patterns {
+		_, err := uuid.Parse(pattern)
 		if err != nil {
-			regexes = append(regexes, filter)
+			regexes = append(regexes, pattern)
 		} else {
-			ids = append(ids, filter)
+			ids = append(ids, pattern)
 		}
 	}
 
diff --git a/src/client_cli/cmdUser/helper.go b/src/client_cli/cmdUser/helper.go
index 001f99bafccdf4052b778569ac28b9f3c79217f7..332c283f26fd9c4ad2e36a5590fd09094f3767db 100644
--- a/src/client_cli/cmdUser/helper.go
+++ b/src/client_cli/cmdUser/helper.go
@@ -53,14 +53,14 @@ Regex examples:
 //   "."   -> matches everything
 //   "bla" -> matches any user containing "bla"
 func printFilteredUsers(c cmd.Command, regexes []string, route string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	if len(regexes) < 1 {
 		c.PrintUsage()
 		return 1
 	}
 
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
 	resp, err := client.R().Get(host+route)
 	if err != nil {
 		u.PrintlnErr("Error: "+err.Error())
diff --git a/src/client_cli/cmdVM/helper.go b/src/client_cli/cmdVM/helper.go
index 5e33c496c6c144673cf6a5e9fe71de19474124c4..83baf9b6dbdab7c4da5951d97dcd2df75c39323d 100644
--- a/src/client_cli/cmdVM/helper.go
+++ b/src/client_cli/cmdVM/helper.go
@@ -87,8 +87,8 @@ func printFilteredVMs(c cmd.Command, args []string, route string) int {
 }
 
 // Returns a list of filtered VMs for a given route.
-// The filter is based on the specified IDs and regexes.
-// The filters argument can either be:
+// Filters are based on patterns describing IDs or regexes.
+// Patterns can either be:
 // - any number of "VM IDs" (UUID)
 // - any number of "VM names"
 // - any combination of "VM IDs" and "VM names"
@@ -98,26 +98,28 @@ func printFilteredVMs(c cmd.Command, args []string, route string) int {
 //   ""    -> matches everything
 //   "."   -> matches everything
 //   "bla" -> matches any VM name containing "bla"
-func getFilteredVMs(route string, filters []string) ([]VM, error) {
+func getFilteredVMs(route string, patterns []string) ([]VM, error) {
+	if len(patterns) < 1 {
+		return nil, errors.New("At least one ID or regex must be specified")
+	}
+
 	client := g.GetInstance().Client
 	host := g.GetInstance().Host
 
 	var ids []string
 	var regexes []string
 
-	for _, filter := range filters {
-		_, err := uuid.Parse(filter)
+	for _, pattern := range patterns {
+		_, err := uuid.Parse(pattern)
 		if err != nil {
-			regexes = append(regexes, filter)
+			regexes = append(regexes, pattern)
 		} else {
-			ids = append(ids, filter)
+			ids = append(ids, pattern)
 		}
 	}
 
 	resp, err := client.R().Get(host+route)
 	if err != nil {
-		//u.PrintlnErr("Error: "+err.Error())
-		//return 1
 		return nil, err
 	}
 
@@ -126,8 +128,6 @@ func getFilteredVMs(route string, filters []string) ([]VM, error) {
 	if resp.IsSuccess() {
 		vms, err := getVMs(resp)
 		if err != nil {
-			//u.PrintlnErr("Error: "+err.Error())
-			//return 1
 			return nil, err
 		}
 
@@ -136,7 +136,6 @@ func getFilteredVMs(route string, filters []string) ([]VM, error) {
 			for _, id := range ids {
 				if id == vm.ID.String() {
 					vmsList = append(vmsList, vm)
-					//u.Println(vm.String())
 					found = true
 					break
 				}
@@ -147,11 +146,9 @@ func getFilteredVMs(route string, filters []string) ([]VM, error) {
 			for _, regex := range regexes {
 				match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(vm.Name))
 				if err != nil {
-					//u.PrintlnErr("Error matching \""+regex+"\": "+err.Error())
 					return nil, errors.New("Error matching \""+regex+"\": "+err.Error())
 				} else {
 					if match {
-						//u.Println(vm.String())
 						vmsList = append(vmsList, vm)
 						break
 					}
@@ -161,8 +158,6 @@ func getFilteredVMs(route string, filters []string) ([]VM, error) {
 		return vmsList, nil
 	} else {
 		return nil, errors.New("Error: "+resp.Status()+": "+resp.String())
-		//u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
-		//return 1
 	}
 }
 
diff --git a/src/client_cli/cmdVM/vmDelAccess.go b/src/client_cli/cmdVM/vmDelAccess.go
index dc09edab8513bcf977d0e8b600335c4a438ca8f6..30398e25e8ab4e9af424bcc0012b326ff4e5637b 100644
--- a/src/client_cli/cmdVM/vmDelAccess.go
+++ b/src/client_cli/cmdVM/vmDelAccess.go
@@ -1,6 +1,8 @@
 package cmdVM
 
 import (
+	"errors"
+	"net/mail"
 	u "nexus-client/utils"
 	g "nexus-client/globals"
 )
@@ -9,17 +11,29 @@ type DelAccess struct {
     Name string
 }
 
+type vmDelAccessParameters struct {
+
+}
+
 func (cmd *DelAccess)GetName() string {
 	return cmd.Name
 }
  
 func (cmd *DelAccess)GetDesc() string {
-	return "Delete the VM access for a given user."
+	return "Delete a user's VM access in one or more VMs (regex matching)."
 }
 
 func (cmd *DelAccess)PrintUsage() {
 	u.PrintlnErr(cmd.GetDesc())
-	u.PrintlnErr("Usage: "+cmd.Name+" vmID email")
+	u.PrintlnErr("Usage: "+cmd.GetName()+" [ID ...] [regex ...] email")
+	const usage string = `Only VMs matching the specified IDs or regexes will have their VM access deleted.
+Any number of IDs or regexes can be specified.
+The regex only matches the VM's name and is case-insensitive.
+Regex examples:
+""    -> matches any VMs
+"."   -> matches any VMs
+"bla" -> matches any VMs containing "bla" in their name`
+	u.PrintlnErr(usage)
 }
 
 func (cmd *DelAccess)Run(args []string) int {
@@ -27,25 +41,67 @@ func (cmd *DelAccess)Run(args []string) int {
 	host := g.GetInstance().Host
 
 	argc := len(args)
-	if argc != 2 {
+	if argc < 1 {
 		cmd.PrintUsage()
 		return 1
 	}
 
-	vmID := args[0]
-	email := args[1]
-
-	resp, err := client.R().Delete(host+"/vms/"+vmID+"/access/"+email)
+	email, patterns, err := cmd.parseArgs(args)
 	if err != nil {
-		u.PrintlnErr("Error: "+err.Error())
+		u.PrintlnErr(err.Error())
 		return 1
 	}
 
-	if resp.IsSuccess() {
-		u.Println(resp)
-		return 0
-	} else {
-		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
+	vms, err := getFilteredVMs("/vms/editaccess", patterns)
+	if err != nil {
+		u.PrintlnErr(err.Error())
 		return 1
 	}
+
+	statusCode := 0
+
+	for _, vm := range(vms) {
+		uuid := vm.ID.String()
+		resp, err := client.R().Delete(host+"/vms/"+uuid+"/access/"+email)
+		if err != nil {
+			u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			statusCode = 1
+		} else {
+			if resp.IsSuccess() {
+				u.Println("Removed VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+")")
+			} else {
+				u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				statusCode = 1
+			}
+		}
+	}
+
+	return statusCode
+}
+
+func (cmd *DelAccess)parseArgs(args []string) (string, []string, error) {
+	var patterns []string
+	emailFound := false
+	var email string
+
+	for _, arg := range args {
+		parsed, err := mail.ParseAddress(arg)
+		if err != nil {
+			// Not an email address, then must be a "pattern" (ID or regex)
+			patterns = append(patterns, arg)
+		} else {
+			// An email address was found
+			if emailFound {
+				return "", nil, errors.New("Only one email address must be specified")
+			}
+			email = parsed.Address
+			emailFound = true
+		}
+	}
+
+	if !emailFound {
+		return "", nil, errors.New("An email address must be specified")
+	}
+
+	return email, patterns, nil
 }
diff --git a/src/client_cli/cmdVM/vmEdit.go b/src/client_cli/cmdVM/vmEdit.go
index c96c178ff5f79877cec4a96c2e9efb6fef85e5f4..c00d85e92cf1ca54aa7c9897d20d72d676f82b45 100644
--- a/src/client_cli/cmdVM/vmEdit.go
+++ b/src/client_cli/cmdVM/vmEdit.go
@@ -30,8 +30,8 @@ func (cmd *Edit)GetDesc() string {
 func (cmd *Edit)PrintUsage() {
 	u.PrintlnErr(cmd.GetDesc())
 	u.PrintlnErr("Usage: "+cmd.GetName()+" [ID ...] [regex ...] [name=\"new name\"] [cpus=n] [ram=n] [nic=none/user]")
-	u.PrintlnErr("Only VMs matching the specified IDs or regexes will be edited.")
-	const usage string = `Any number of IDs or regexes can be specified.
+	const usage string = `Only VMs matching the specified IDs or regexes will be edited.
+Any number of IDs or regexes can be specified.
 The regex only matches the VM's name and is case-insensitive.
 Regex examples:
 ""    -> matches any VMs
@@ -44,17 +44,57 @@ nic     must be either "none" or "user`
 	u.PrintlnErr(usage)
 }
 
-func (cmd *Edit)getStringVal(s string, prefix string) string {
-	if strings.HasPrefix(s, prefix) {
-		parts := strings.Split(s, prefix)
-		return parts[1]
+func (cmd *Edit)Run(args []string) int {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	argc := len(args)
+	if argc < 1 {
+		cmd.PrintUsage()
+		return 1
 	}
-	return ""
+
+	vmParams, patterns, err := cmd.parseArgs(args)
+	if err != nil {
+		u.PrintlnErr(err.Error())
+		return 1
+	}
+
+	if vmParams == nil {
+		cmd.PrintUsage()
+		return 1
+	}
+
+	vms, err := getFilteredVMs("/vms/edit", patterns)
+	if err != nil {
+		u.PrintlnErr(err.Error())
+		return 1
+	}
+
+	statusCode := 0
+
+	for _, vm := range(vms) {
+		uuid := vm.ID.String()
+		resp, err := client.R().SetBody(vmParams).Put(host+"/vms/"+uuid)
+		if err != nil {
+			u.PrintlnErr("Failed editing VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			statusCode = 1
+		} else {
+			if resp.IsSuccess() {
+				u.Println("Edited VM \""+vm.Name+"\" ("+uuid+")")
+			} else {
+				u.PrintlnErr("Failed editing VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				statusCode = 1
+			}
+		}
+	}
+
+	return statusCode
 }
 
 func (cmd *Edit)parseArgs(args []string) (*vmEditParameters, []string, error) {
 	vmParams := &vmEditParameters {}
-	var regex []string
+	var patterns []string
 	atLeastOneArg := false
 
 	for _, arg := range args {
@@ -91,61 +131,20 @@ func (cmd *Edit)parseArgs(args []string) (*vmEditParameters, []string, error) {
 			continue
 		}
 
-		regex = append(regex, arg)
+		patterns = append(patterns, arg)
 	}
 
 	if atLeastOneArg {
-		return vmParams, regex, nil
+		return vmParams, patterns, nil
 	} else {
 		return nil, nil, nil
 	}
 }
 
-func (cmd *Edit)Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	argc := len(args)
-	if argc < 1 {
-		cmd.PrintUsage()
-		return 1
-	}
-
-	vmParams, regex, err := cmd.parseArgs(args)
-	if err != nil {
-		u.PrintlnErr(err.Error())
-		return 1
-	}
-
-	if vmParams == nil {
-		cmd.PrintUsage()
-		return 1
-	}
-
-	vms, err := getFilteredVMs("/vms/edit", regex)
-	if err != nil {
-		u.PrintlnErr(err.Error())
-		return 1
-	}
-
-	statusCode := 0
-
-	for _, vm := range(vms) {
-		uuid := vm.ID.String()
-		resp, err := client.R().SetBody(vmParams).Put(host+"/vms/"+uuid)
-		if err != nil {
-			u.PrintlnErr("Failed editing VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
-			statusCode = 1
-		} else {
-			if resp.IsSuccess() {
-				u.Println("Edited VM \""+vm.Name+"\" ("+uuid+")")
-			} else {
-				u.PrintlnErr("Failed editing VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
-				statusCode = 1
-			}
-		}
-
+func (cmd *Edit)getStringVal(s string, prefix string) string {
+	if strings.HasPrefix(s, prefix) {
+		parts := strings.Split(s, prefix)
+		return parts[1]
 	}
-
-	return statusCode
+	return ""
 }
diff --git a/src/client_cli/cmdVM/vmSetAccess.go b/src/client_cli/cmdVM/vmSetAccess.go
index c8ce79531347583b192e1a8e0975b1f6f8a164c5..5d8ba08483c66cc5819ebc02a3287509dd390ea5 100644
--- a/src/client_cli/cmdVM/vmSetAccess.go
+++ b/src/client_cli/cmdVM/vmSetAccess.go
@@ -1,6 +1,8 @@
 package cmdVM
 
 import (
+	"errors"
+	"net/mail"
 	u "nexus-client/utils"
 	g "nexus-client/globals"
 )
@@ -14,12 +16,21 @@ func (cmd *SetAccess)GetName() string {
 }
  
 func (cmd *SetAccess)GetDesc() string {
-	return "Set the VM access for a given user."
+	return "Set a user's VM access in one or more VMs (regex matching)."
 }
 
 func (cmd *SetAccess)PrintUsage() {
 	u.PrintlnErr(cmd.GetDesc())
-	u.PrintlnErr("Usage: "+cmd.Name+" vmID email [capability ...]")
+	u.PrintlnErr("Usage: "+cmd.GetName()+" [ID ...] [regex ...] email [capability ...]")
+	const usage string = `Capabilities must be specified after the email.
+Only VMs matching the specified IDs or regexes will have their VM access set.
+Any number of IDs or regexes can be specified.
+The regex only matches the VM's name and is case-insensitive.
+Regex examples:
+""    -> matches any VMs
+"."   -> matches any VMs
+"bla" -> matches any VMs containing "bla" in their name`
+	u.PrintlnErr(usage)
 }
 
 func (cmd *SetAccess)Run(args []string) int {
@@ -27,34 +38,80 @@ func (cmd *SetAccess)Run(args []string) int {
 	host := g.GetInstance().Host
 
 	argc := len(args)
-	if argc < 2 {
+	if argc < 1 {
 		cmd.PrintUsage()
 		return 1
 	}
 
-	vmID := args[0]
-	email := args[1]
+	email, caps, patterns, err := cmd.parseArgs(args)
+	if err != nil {
+		u.PrintlnErr(err.Error())
+		return 1
+	}
 
-	type VMAccessForUserArgs struct {
+	vms, err := getFilteredVMs("/vms/editaccess", patterns)
+	if err != nil {
+		u.PrintlnErr(err.Error())
+		return 1
+	}
+
+	type vmAccessForUserArgs struct {
 		Access map[string]int `json:"access"  validate:"required"`
 	}
-	
-	userUpdatedAccessArgs := &VMAccessForUserArgs { make(map[string]int) }
-	for _, cap := range args[2:] {
+
+	userUpdatedAccessArgs := &vmAccessForUserArgs { make(map[string]int) }
+	for _, cap := range caps {
 		userUpdatedAccessArgs.Access[cap] = 1
 	}
 
-	resp, err := client.R().SetBody(userUpdatedAccessArgs).Put(host+"/vms/"+vmID+"/access/"+email)
-	if err != nil {
-		u.PrintlnErr("Error: "+err.Error())
-		return 1
+	statusCode := 0
+
+	for _, vm := range(vms) {
+		uuid := vm.ID.String()
+		resp, err := client.R().SetBody(userUpdatedAccessArgs).Put(host+"/vms/"+uuid+"/access/"+email)
+		if err != nil {
+			u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			statusCode = 1
+		} else {
+			if resp.IsSuccess() {
+				u.Println("Removed VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+")")
+			} else {
+				u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				statusCode = 1
+			}
+		}
 	}
 
-	if resp.IsSuccess() {
-		u.Println(resp)
-		return 0
-	} else {
-		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
-		return 1
+	return statusCode
+}
+
+func (cmd *SetAccess)parseArgs(args []string) (string, []string, []string, error) {
+	var patterns []string
+	var capabilities []string
+	emailFound := false
+	var email string
+
+	for _, arg := range args {
+		// Before or after the email addres?
+		if !emailFound {
+			// Before the email address: it's either a "pattern" (ID or regex) or an email
+			parsed, err := mail.ParseAddress(arg)
+			if err != nil {
+				// Not an email address, then must be a "pattern" (ID or regex)
+				patterns = append(patterns, arg)
+			} else {
+				email = parsed.Address
+				emailFound = true
+			}
+		} else {
+			// After the email address: it's a capability
+			capabilities = append(capabilities, arg)
+		}
+	}
+
+	if !emailFound {
+		return "", nil, nil, errors.New("An email address must be specified")
 	}
+
+	return email, capabilities, patterns, nil
 }