This is an automated email from the ASF dual-hosted git repository.

weizhou pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.18 by this push:
     new 78bdde9e981 AutoScaling: support Managed User Data (#7769)
78bdde9e981 is described below

commit 78bdde9e981ef55f277327bf0e783e215d5af9c4
Author: Wei Zhou <[email protected]>
AuthorDate: Tue Aug 22 11:07:16 2023 +0200

    AutoScaling: support Managed User Data (#7769)
---
 .../com/cloud/network/as/AutoScaleVmProfile.java   |   4 +
 .../java/org/apache/cloudstack/api/BaseCmd.java    |  17 ++
 .../autoscale/CreateAutoScaleVmProfileCmd.java     |  15 ++
 .../autoscale/UpdateAutoScaleVmProfileCmd.java     |  17 ++
 .../api/command/user/vm/DeployVMCmd.java           |  26 +--
 .../api/command/user/vm/ResetVMUserDataCmd.java    |  16 +-
 .../cloudstack/api/command/user/vm/ScaleVMCmd.java |  15 +-
 .../api/command/user/vm/UpdateVMCmd.java           |  14 +-
 .../api/command/user/vm/UpgradeVMCmd.java          |  15 +-
 .../api/response/AutoScaleVmProfileResponse.java   |  48 ++++
 .../com/cloud/network/as/AutoScaleVmProfileVO.java |  24 ++
 .../com/cloud/upgrade/dao/Upgrade41800to41810.java |   5 +
 .../resources/META-INF/db/schema-41800to41810.sql  |   4 +
 .../cloud/network/as/AutoScaleVmProfileVOTest.java |  31 +++
 .../java/com/cloud/api/ApiAsyncJobDispatcher.java  |   6 +
 .../main/java/com/cloud/api/ApiResponseHelper.java |  14 ++
 server/src/main/java/com/cloud/api/ApiServer.java  |   3 +
 .../com/cloud/network/as/AutoScaleManagerImpl.java |  53 ++++-
 .../src/main/java/com/cloud/vm/UserVmManager.java  |   4 +
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |   6 +-
 .../java/com/cloud/api/ApiResponseHelperTest.java  |  72 ++++++
 .../cloud/network/as/AutoScaleManagerImplTest.java |  75 +++++-
 test/integration/smoke/test_vm_autoscaling.py      |  69 ++++++
 ui/public/locales/en.json                          |   1 +
 ui/src/views/compute/AutoScaleVmProfile.vue        |  92 ++++++--
 ui/src/views/compute/CreateAutoScaleVmGroup.vue    | 257 +++++++++++++++++++--
 ui/src/views/compute/ResetUserData.vue             |  10 +-
 27 files changed, 785 insertions(+), 128 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/as/AutoScaleVmProfile.java 
b/api/src/main/java/com/cloud/network/as/AutoScaleVmProfile.java
index afeba0bd749..68d18a0edc9 100644
--- a/api/src/main/java/com/cloud/network/as/AutoScaleVmProfile.java
+++ b/api/src/main/java/com/cloud/network/as/AutoScaleVmProfile.java
@@ -35,6 +35,10 @@ public interface AutoScaleVmProfile extends 
ControlledEntity, InternalIdentity,
 
     String getUserData();
 
+    Long getUserDataId();
+
+    String getUserDataDetails();
+
     public String getUuid();
 
     public Long getZoneId();
diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java
index e431f63557c..79e103a291d 100644
--- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java
@@ -21,8 +21,10 @@ import java.lang.reflect.Field;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
@@ -42,6 +44,7 @@ import 
org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
 import org.apache.cloudstack.query.QueryService;
 import org.apache.cloudstack.storage.ImageStoreService;
 import org.apache.cloudstack.usage.UsageService;
+import org.apache.commons.collections.MapUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.configuration.ConfigurationService;
@@ -456,4 +459,18 @@ public abstract class BaseCmd {
         return ApiCommandResourceType.None;
     }
 
+    public Map<String, String> convertDetailsToMap(Map details) {
+        Map<String, String> detailsMap = new HashMap<String, String>();
+        if (MapUtils.isNotEmpty(details)) {
+            Collection parameterCollection = details.values();
+            Iterator iter = parameterCollection.iterator();
+            while (iter.hasNext()) {
+                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
+                for (Map.Entry<String,String> entry: value.entrySet()) {
+                    detailsMap.put(entry.getKey(),entry.getValue());
+                }
+            }
+        }
+        return detailsMap;
+    }
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java
index ba7149ea3d8..db6ccd9ce53 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java
@@ -35,6 +35,7 @@ import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.ProjectResponse;
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
 import org.apache.cloudstack.api.response.TemplateResponse;
+import org.apache.cloudstack.api.response.UserDataResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.context.CallContext;
@@ -107,6 +108,12 @@ public class CreateAutoScaleVmProfileCmd extends 
BaseAsyncCreateCmd {
             since = "4.18.0")
     private String userData;
 
+    @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, 
entityType = UserDataResponse.class, description = "the ID of the Userdata", 
since = "4.18.1")
+    private Long userDataId;
+
+    @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, 
description = "used to specify the parameters values for the variables in 
userdata.", since = "4.18.1")
+    private Map userDataDetails;
+
     @Parameter(name = ApiConstants.AUTOSCALE_USER_ID,
                type = CommandType.UUID,
                entityType = UserResponse.class,
@@ -163,6 +170,14 @@ public class CreateAutoScaleVmProfileCmd extends 
BaseAsyncCreateCmd {
         return userData;
     }
 
+    public Long getUserDataId() {
+        return userDataId;
+    }
+
+    public Map<String, String> getUserDataDetails() {
+        return convertDetailsToMap(userDataDetails);
+    }
+
     public Long getAutoscaleUserId() {
         return autoscaleUserId;
     }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java
index c0b385932a9..3e65d38e520 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java
@@ -35,6 +35,7 @@ import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
 import org.apache.cloudstack.api.response.TemplateResponse;
+import org.apache.cloudstack.api.response.UserDataResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.context.CallContext;
 
@@ -102,6 +103,14 @@ public class UpdateAutoScaleVmProfileCmd extends 
BaseAsyncCustomIdCmd {
             since = "4.18.0")
     private String userData;
 
+    @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, 
entityType = UserDataResponse.class, description = "the ID of the userdata",
+            since = "4.18.1")
+    private Long userDataId;
+
+    @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, 
description = "used to specify the parameters values for the variables in 
userdata.",
+            since = "4.18.1")
+    private Map userDataDetails;
+
     @Parameter(name = ApiConstants.AUTOSCALE_USER_ID,
                type = CommandType.UUID,
                entityType = UserResponse.class,
@@ -156,6 +165,14 @@ public class UpdateAutoScaleVmProfileCmd extends 
BaseAsyncCustomIdCmd {
         return userData;
     }
 
+    public Long getUserDataId() {
+        return userDataId;
+    }
+
+    public Map<String, String> getUserDataDetails() {
+        return convertDetailsToMap(userDataDetails);
+    }
+
     public Long getAutoscaleUserId() {
         return autoscaleUserId;
     }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 12212dac417..ef6dba63166 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -309,17 +309,8 @@ public class DeployVMCmd extends 
BaseAsyncCreateCustomIdCmd implements SecurityG
     }
 
     public Map<String, String> getDetails() {
-        Map<String, String> customparameterMap = new HashMap<String, String>();
-        if (details != null && details.size() != 0) {
-            Collection parameterCollection = details.values();
-            Iterator iter = parameterCollection.iterator();
-            while (iter.hasNext()) {
-                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
-                for (Map.Entry<String,String> entry: value.entrySet()) {
-                    customparameterMap.put(entry.getKey(),entry.getValue());
-                }
-            }
-        }
+        Map<String, String> customparameterMap = convertDetailsToMap(details);
+
         if (getBootType() != null) {
             customparameterMap.put(getBootType().toString(), 
getBootMode().toString());
         }
@@ -450,18 +441,7 @@ public class DeployVMCmd extends 
BaseAsyncCreateCustomIdCmd implements SecurityG
     }
 
     public Map<String, String> getUserdataDetails() {
-        Map<String, String> userdataDetailsMap = new HashMap<String, String>();
-        if (userdataDetails != null && userdataDetails.size() != 0) {
-            Collection parameterCollection = userdataDetails.values();
-            Iterator iter = parameterCollection.iterator();
-            while (iter.hasNext()) {
-                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
-                for (Map.Entry<String,String> entry: value.entrySet()) {
-                    userdataDetailsMap.put(entry.getKey(),entry.getValue());
-                }
-            }
-        }
-        return userdataDetailsMap;
+        return convertDetailsToMap(userdataDetails);
     }
 
     public Long getZoneId() {
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java
index ad0592ca4ac..3ead67e2106 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMUserDataCmd.java
@@ -39,9 +39,6 @@ import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.log4j.Logger;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 
 @APICommand(name = "resetUserDataForVirtualMachine", responseObject = 
UserVmResponse.class, description = "Resets the UserData for virtual machine. " 
+
@@ -117,18 +114,7 @@ public class ResetVMUserDataCmd extends BaseCmd implements 
UserCmd {
     }
 
     public Map<String, String> getUserdataDetails() {
-        Map<String, String> userdataDetailsMap = new HashMap<String, String>();
-        if (userdataDetails != null && userdataDetails.size() != 0) {
-            Collection parameterCollection = userdataDetails.values();
-            Iterator iter = parameterCollection.iterator();
-            while (iter.hasNext()) {
-                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
-                for (Map.Entry<String,String> entry: value.entrySet()) {
-                    userdataDetailsMap.put(entry.getKey(),entry.getValue());
-                }
-            }
-        }
-        return userdataDetailsMap;
+        return convertDetailsToMap(userdataDetails);
     }
 
     @Override
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java
index e60bce2810a..5af45762ece 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java
@@ -16,9 +16,6 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.vm;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -99,17 +96,7 @@ public class ScaleVMCmd extends BaseAsyncCmd implements 
UserCmd {
     //it is because details.values() cannot be cast to a map.
     //it gives a exception
     public Map<String, String> getDetails() {
-        Map<String, String> customparameterMap = new HashMap<String, String>();
-        if (details != null && details.size() != 0) {
-            Collection parameterCollection = details.values();
-            Iterator iter = parameterCollection.iterator();
-            while (iter.hasNext()) {
-                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
-                for (String key : value.keySet()) {
-                    customparameterMap.put(key, value.get(key));
-                }
-            }
-        }
+        Map<String, String> customparameterMap = convertDetailsToMap(details);
 
         if (shrinkOk != null) customparameterMap.put(ApiConstants.SHRINK_OK, 
String.valueOf(isShrinkOk()));
         if (autoMigrate != null) 
customparameterMap.put(ApiConstants.AUTO_MIGRATE, 
String.valueOf(getAutoMigrate()));
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
index 3e0ef75ecdd..32ce1f6db52 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
@@ -18,7 +18,6 @@ package org.apache.cloudstack.api.command.user.vm;
 
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -176,18 +175,7 @@ public class UpdateVMCmd extends BaseCustomIdCmd 
implements SecurityGroupAction,
     }
 
     public Map<String, String> getUserdataDetails() {
-        Map<String, String> userdataDetailsMap = new HashMap<String, String>();
-        if (userdataDetails != null && userdataDetails.size() != 0) {
-            Collection parameterCollection = userdataDetails.values();
-            Iterator iter = parameterCollection.iterator();
-            while (iter.hasNext()) {
-                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
-                for (Map.Entry<String,String> entry: value.entrySet()) {
-                    userdataDetailsMap.put(entry.getKey(),entry.getValue());
-                }
-            }
-        }
-        return userdataDetailsMap;
+        return convertDetailsToMap(userdataDetails);
     }
 
     public Boolean getDisplayVm() {
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java
index b6acd71b4c4..4b31c12ec0a 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java
@@ -16,9 +16,6 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.vm;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 
 import org.apache.log4j.Logger;
@@ -95,17 +92,7 @@ public class UpgradeVMCmd extends BaseCmd implements UserCmd 
{
     }
 
     public Map<String, String> getDetails() {
-        Map<String, String> customparameterMap = new HashMap<String, String>();
-        if (details != null && details.size() != 0) {
-            Collection parameterCollection = details.values();
-            Iterator iter = parameterCollection.iterator();
-            while (iter.hasNext()) {
-                HashMap<String, String> value = (HashMap<String, 
String>)iter.next();
-                for (String key : value.keySet()) {
-                    customparameterMap.put(key, value.get(key));
-                }
-            }
-        }
+        Map<String, String> customparameterMap = convertDetailsToMap(details);
 
         if (shrinkOk != null) customparameterMap.put(ApiConstants.SHRINK_OK, 
String.valueOf(isShrinkOk()));
         if (autoMigrate != null) 
customparameterMap.put(ApiConstants.AUTO_MIGRATE, 
String.valueOf(getAutoMigrate()));
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java
 
b/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java
index 66d7296f406..9f238344730 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java
@@ -72,6 +72,18 @@ public class AutoScaleVmProfileResponse extends BaseResponse 
implements Controll
     @Param(description = "Base 64 encoded VM user data")
     private String userData;
 
+    @SerializedName(ApiConstants.USER_DATA_ID) @Param(description="the id of 
userdata used for the VM", since = "4.18.1")
+    private String userDataId;
+
+    @SerializedName(ApiConstants.USER_DATA_NAME) @Param(description="the name 
of userdata used for the VM", since = "4.18.1")
+    private String userDataName;
+
+    @SerializedName(ApiConstants.USER_DATA_POLICY) @Param(description="the 
userdata override policy with the userdata provided while deploying VM", since 
= "4.18.1")
+    private String userDataPolicy;
+
+    @SerializedName(ApiConstants.USER_DATA_DETAILS) @Param(description="list 
of variables and values for the variables declared in userdata", since = 
"4.18.1")
+    private String userDataDetails;
+
     @SerializedName(ApiConstants.AUTOSCALE_USER_ID)
     @Param(description = "the ID of the user used to launch and destroy the 
VMs")
     private String autoscaleUserId;
@@ -153,6 +165,22 @@ public class AutoScaleVmProfileResponse extends 
BaseResponse implements Controll
         this.userData = userData;
     }
 
+    public void setUserDataId(String userDataId) {
+        this.userDataId = userDataId;
+    }
+
+    public void setUserDataName(String userDataName) {
+        this.userDataName = userDataName;
+    }
+
+    public void setUserDataPolicy(String userDataPolicy) {
+        this.userDataPolicy = userDataPolicy;
+    }
+
+    public void setUserDataDetails(String userDataDetails) {
+        this.userDataDetails = userDataDetails;
+    }
+
     @Override
     public void setAccountName(String accountName) {
         this.accountName = accountName;
@@ -193,4 +221,24 @@ public class AutoScaleVmProfileResponse extends 
BaseResponse implements Controll
     public void setForDisplay(Boolean forDisplay) {
         this.forDisplay = forDisplay;
     }
+
+    public String getUserData() {
+        return userData;
+    }
+
+    public String getUserDataId() {
+        return userDataId;
+    }
+
+    public String getUserDataName() {
+        return userDataName;
+    }
+
+    public String getUserDataPolicy() {
+        return userDataPolicy;
+    }
+
+    public String getUserDataDetails() {
+        return userDataDetails;
+    }
 }
diff --git 
a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java 
b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java
index 0a0ba1e62aa..21291062756 100644
--- a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java
@@ -88,6 +88,12 @@ public class AutoScaleVmProfileVO implements 
AutoScaleVmProfile, Identity, Inter
     @Basic(fetch = FetchType.LAZY)
     private String userData;
 
+    @Column(name = "user_data_id", nullable = true)
+    private Long userDataId = null;
+
+    @Column(name = "user_data_details", updatable = true, length = 4096)
+    private String userDataDetails;
+
     @Column(name = GenericDao.REMOVED_COLUMN)
     protected Date removed;
 
@@ -228,6 +234,24 @@ public class AutoScaleVmProfileVO implements 
AutoScaleVmProfile, Identity, Inter
         return userData;
     }
 
+    @Override
+    public Long getUserDataId() {
+        return userDataId;
+    }
+
+    public void setUserDataId(Long userDataId) {
+        this.userDataId = userDataId;
+    }
+
+    @Override
+    public String getUserDataDetails() {
+        return userDataDetails;
+    }
+
+    public void setUserDataDetails(String userDataDetails) {
+        this.userDataDetails = userDataDetails;
+    }
+
     @Override
     public String getUuid() {
         return uuid;
diff --git 
a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java 
b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java
index a2733215060..53bd497ca22 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java
@@ -63,6 +63,7 @@ public class Upgrade41800to41810 implements DbUpgrade, 
DbUpgradeSystemVmTemplate
         fixForeignKeyNames(conn);
         updateGuestOsMappings(conn);
         copyGuestOsMappingsToVMware80u1();
+        addForeignKeyToAutoscaleVmprofiles(conn);
     }
 
     @Override
@@ -225,4 +226,8 @@ public class Upgrade41800to41810 implements DbUpgrade, 
DbUpgradeSystemVmTemplate
         DbUpgradeUtils.dropKeysIfExist(conn, "cloud.volumes", keys, false);
         DbUpgradeUtils.addForeignKey(conn, "volumes", 
"passphrase_id","passphrase", "id");
     }
+
+    private void addForeignKeyToAutoscaleVmprofiles(Connection conn) {
+        DbUpgradeUtils.addForeignKey(conn, "autoscale_vmprofiles", 
"user_data_id", "user_data", "id");
+    }
 }
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
index e07e12871f3..495c184918c 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
@@ -35,6 +35,10 @@ CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 
2022 (64-bit)', 'VM
 CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2022 (64-bit)', 
'VMware', '8.0.0.1', 'windows2019srvNext_64Guest');
 CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2022 (64-bit)', 
'Xenserver', '8.2.0', 'Windows Server 2022 (64-bit)');
 
+-- Support userdata ids and details in VM AutoScaling
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.autoscale_vmprofiles', 
'user_data_id', 'bigint unsigned DEFAULT NULL COMMENT "id of the user data" 
AFTER `user_data`');
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.autoscale_vmprofiles', 
'user_data_details', 'mediumtext DEFAULT NULL COMMENT "value of the 
comma-separated list of parameters" AFTER `user_data_id`');
+
 -- Don't enable CPU cap for default system offerings, fixes regression from 
https://github.com/apache/cloudstack/pull/6420
 UPDATE `cloud`.`service_offering` so
 SET so.limit_cpu_use = 0
diff --git 
a/engine/schema/src/test/java/com/cloud/network/as/AutoScaleVmProfileVOTest.java
 
b/engine/schema/src/test/java/com/cloud/network/as/AutoScaleVmProfileVOTest.java
index 6f49c3d4e1e..7e9658e1dd3 100755
--- 
a/engine/schema/src/test/java/com/cloud/network/as/AutoScaleVmProfileVOTest.java
+++ 
b/engine/schema/src/test/java/com/cloud/network/as/AutoScaleVmProfileVOTest.java
@@ -26,6 +26,18 @@ import org.junit.Test;
 
 public class AutoScaleVmProfileVOTest {
 
+    static long zoneId = 1L;
+    static long domainId = 2L;
+    static long accountId = 3L;
+    static long serviceOfferingId = 4L;
+    static long templateId  = 5L;
+    static String userdata = "userdata";
+    static long userdataId = 6L;
+    static String userdataDetails = "userdataDetails";
+    static String userdataNew = "userdataNew";
+
+    static long autoScaleUserId = 7L;
+
     @Test
     public void testCounterParamsForUpdate() {
         AutoScaleVmProfileVO profile = new AutoScaleVmProfileVO();
@@ -62,4 +74,23 @@ public class AutoScaleVmProfileVOTest {
         Assert.assertEquals("rootdisksize", 
otherDeployParamsList.get(1).first());
         Assert.assertEquals("10", otherDeployParamsList.get(1).second());
     }
+
+    @Test
+    public void testProperties() {
+        AutoScaleVmProfileVO profile = new AutoScaleVmProfileVO(zoneId, 
domainId, accountId, serviceOfferingId, templateId, null, null, userdata, null, 
autoScaleUserId);
+        Assert.assertEquals(new Long(zoneId), profile.getZoneId());
+        Assert.assertEquals(domainId, profile.getDomainId());
+        Assert.assertEquals(accountId, profile.getAccountId());
+        Assert.assertEquals(new Long(serviceOfferingId), 
profile.getServiceOfferingId());
+        Assert.assertEquals(new Long(templateId), profile.getTemplateId());
+        Assert.assertEquals(userdata, profile.getUserData());
+        Assert.assertEquals(new Long(autoScaleUserId), 
profile.getAutoScaleUserId());
+
+        profile.setUserData(userdataNew);
+        profile.setUserDataId(userdataId);
+        profile.setUserDataDetails(userdataDetails);
+        Assert.assertEquals(userdataNew, profile.getUserData());
+        Assert.assertEquals(new Long(userdataId), profile.getUserDataId());
+        Assert.assertEquals(userdataDetails, profile.getUserDataDetails());
+    }
 }
diff --git a/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java 
b/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java
index b596254994c..e09e95e2ce6 100644
--- a/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java
+++ b/server/src/main/java/com/cloud/api/ApiAsyncJobDispatcher.java
@@ -21,6 +21,7 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.BaseAsyncCmd;
 import org.apache.cloudstack.api.BaseAsyncCreateCmd;
@@ -101,6 +102,11 @@ public class ApiAsyncJobDispatcher extends AdapterBase 
implements AsyncJobDispat
                 ctx.putContextParameters((Map<Object, Object>) 
gson.fromJson(contextDetails, objectMapType));
             }
 
+            String httpmethod = params.get(ApiConstants.HTTPMETHOD);
+            if (httpmethod != null) {
+                cmdObj.setHttpMethod(httpmethod);
+            }
+
             try {
                 // dispatch could ultimately queue the job
                 _dispatcher.dispatch(cmdObj, params, true);
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java 
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 8fffebb3303..55f00a609e9 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -365,6 +365,7 @@ import com.cloud.user.User;
 import com.cloud.user.UserAccount;
 import com.cloud.user.UserData;
 import com.cloud.user.UserStatisticsVO;
+import com.cloud.user.dao.UserDataDao;
 import com.cloud.user.dao.UserStatisticsDao;
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.Pair;
@@ -455,6 +456,8 @@ public class ApiResponseHelper implements ResponseGenerator 
{
     UserVmJoinDao userVmJoinDao;
     @Inject
     NetworkServiceMapDao ntwkSrvcDao;
+    @Inject
+    UserDataDao userDataDao;
 
     @Override
     public UserResponse createUserResponse(User user) {
@@ -3393,9 +3396,20 @@ public class ApiResponseHelper implements 
ResponseGenerator {
             VMTemplateVO template = 
ApiDBUtils.findTemplateById(profile.getTemplateId());
             if (template != null) {
                 response.setTemplateId(template.getUuid());
+                if (template.getUserDataOverridePolicy() != null) {
+                    
response.setUserDataPolicy(template.getUserDataOverridePolicy().toString());
+                }
             }
         }
         response.setUserData(profile.getUserData());
+        if (profile.getUserDataId() != null) {
+            UserData userData = userDataDao.findById(profile.getUserDataId());
+            if (userData != null) {
+                response.setUserDataId(userData.getUuid());
+                response.setUserDataName(userData.getName());
+            }
+        }
+        response.setUserDataDetails(profile.getUserDataDetails());
         response.setOtherDeployParams(profile.getOtherDeployParamsList());
         response.setCounterParams(profile.getCounterParams());
         response.setExpungeVmGracePeriod(profile.getExpungeVmGracePeriod());
diff --git a/server/src/main/java/com/cloud/api/ApiServer.java 
b/server/src/main/java/com/cloud/api/ApiServer.java
index b62e59f5c27..e88d7cf8b53 100644
--- a/server/src/main/java/com/cloud/api/ApiServer.java
+++ b/server/src/main/java/com/cloud/api/ApiServer.java
@@ -740,6 +740,9 @@ public class ApiServer extends ManagerBase implements 
HttpRequestHandler, ApiSer
             params.put("ctxStartEventId", String.valueOf(startEventId));
             params.put("cmdEventType", asyncCmd.getEventType().toString());
             params.put("ctxDetails", 
ApiGsonHelper.getBuilder().create().toJson(ctx.getContextParameters()));
+            if (asyncCmd.getHttpMethod() != null) {
+                params.put(ApiConstants.HTTPMETHOD, 
asyncCmd.getHttpMethod().toString());
+            }
 
             Long instanceId = (objectId == null) ? asyncCmd.getApiResourceId() 
: objectId;
 
diff --git 
a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java 
b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
index 29cc4cead0b..de8a3ff3c83 100644
--- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
@@ -532,7 +532,6 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
         long zoneId = cmd.getZoneId();
         long serviceOfferingId = cmd.getServiceOfferingId();
         Long autoscaleUserId = cmd.getAutoscaleUserId();
-        String userData = cmd.getUserData();
 
         DataCenter zone = entityMgr.findById(DataCenter.class, zoneId);
 
@@ -545,6 +544,11 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
             throw new InvalidParameterValueException("Unable to find service 
offering by id");
         }
 
+        VirtualMachineTemplate template = 
entityMgr.findById(VirtualMachineTemplate.class, cmd.getTemplateId());
+        if (template == null) {
+            throw new InvalidParameterValueException("Unable to find template 
by id " + cmd.getTemplateId());
+        }
+
         // validations
         HashMap<String, String> deployParams = cmd.getDeployParamMap();
         /*
@@ -562,9 +566,23 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
             profileVO.setDisplay(cmd.getDisplay());
         }
 
+        String userData = cmd.getUserData();
+        Long userDataId = cmd.getUserDataId();
+        String userDataDetails = null;
+        if (MapUtils.isNotEmpty(cmd.getUserDataDetails())) {
+            userDataDetails = cmd.getUserDataDetails().toString();
+        }
+        userData = userVmMgr.finalizeUserData(userData, userDataId, template);
+        userData = userVmMgr.validateUserData(userData, cmd.getHttpMethod());
         if (userData != null) {
             profileVO.setUserData(userData);
         }
+        if (userDataId != null) {
+            profileVO.setUserDataId(userDataId);
+        }
+        if (userDataDetails != null) {
+            profileVO.setUserDataDetails(userDataDetails);
+        }
 
         profileVO = checkValidityAndPersist(profileVO, true);
         s_logger.info("Successfully create AutoScale Vm Profile with Id: " + 
profileVO.getId());
@@ -582,12 +600,19 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
         Map<String, HashMap<String, String>> otherDeployParams = 
cmd.getOtherDeployParams();
         Map counterParamList = cmd.getCounterParamList();
         String userData = cmd.getUserData();
+        Long userDataId = cmd.getUserDataId();
+        String userDataDetails = null;
+        if (MapUtils.isNotEmpty(cmd.getUserDataDetails())) {
+            userDataDetails = cmd.getUserDataDetails().toString();
+        }
+        boolean userdataUpdate = userData != null || userDataId != null || 
MapUtils.isNotEmpty(cmd.getUserDataDetails());
 
         Integer expungeVmGracePeriod = cmd.getExpungeVmGracePeriod();
 
         AutoScaleVmProfileVO vmProfile = 
getEntityInDatabase(CallContext.current().getCallingAccount(), "Auto Scale Vm 
Profile", profileId, autoScaleVmProfileDao);
 
-        boolean physicalParameterUpdate = (templateId != null || 
autoscaleUserId != null || counterParamList != null || otherDeployParams != 
null || expungeVmGracePeriod != null || userData != null);
+        boolean physicalParameterUpdate = (templateId != null || 
autoscaleUserId != null || counterParamList != null
+                || otherDeployParams != null || expungeVmGracePeriod != null 
|| userdataUpdate);
 
         if (serviceOfferingId != null) {
             vmProfile.setServiceOfferingId(serviceOfferingId);
@@ -609,10 +634,6 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
             vmProfile.setCounterParamsForUpdate(counterParamList);
         }
 
-        if (userData != null) {
-            vmProfile.setUserData(userData);
-        }
-
         if (expungeVmGracePeriod != null) {
             vmProfile.setExpungeVmGracePeriod(expungeVmGracePeriod);
         }
@@ -625,6 +646,18 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
             vmProfile.setDisplay(cmd.getDisplay());
         }
 
+        if (userdataUpdate) {
+            if (templateId == null) {
+                templateId = vmProfile.getTemplateId();
+            }
+            VirtualMachineTemplate template = 
entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, templateId);
+            userData = userVmMgr.finalizeUserData(userData, userDataId, 
template);
+            userData = userVmMgr.validateUserData(userData, 
cmd.getHttpMethod());
+            vmProfile.setUserDataId(userDataId);
+            vmProfile.setUserData(userData);
+            vmProfile.setUserDataDetails(userDataDetails);
+        }
+
         List<AutoScaleVmGroupVO> vmGroupList = 
autoScaleVmGroupDao.listByAll(null, profileId);
         for (AutoScaleVmGroupVO vmGroupVO : vmGroupList) {
             if (physicalParameterUpdate && 
!vmGroupVO.getState().equals(AutoScaleVmGroup.State.DISABLED)) {
@@ -1740,6 +1773,8 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
             }
 
             String userData = profileVo.getUserData();
+            Long userDataId = profileVo.getUserDataId();
+            String userDataDetails = profileVo.getUserDataDetails();
 
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
@@ -1763,20 +1798,20 @@ public class AutoScaleManagerImpl extends ManagerBase 
implements AutoScaleManage
             if (zone.getNetworkType() == NetworkType.Basic) {
                 vm = 
userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, 
template, null, owner, vmHostName,
                         vmHostName, diskOfferingId, dataDiskSize, null,
-                        hypervisorType, HTTPMethod.GET, userData, null, null, 
sshKeyPairs,
+                        hypervisorType, HTTPMethod.GET, userData, userDataId, 
userDataDetails, sshKeyPairs,
                         null, null, true, null, affinityGroupIdList, 
customParameters, null, null, null,
                         null, true, overrideDiskOfferingId);
             } else {
                 if (zone.isSecurityGroupEnabled()) {
                     vm = 
userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, 
template, networkIds, null,
                             owner, vmHostName,vmHostName, diskOfferingId, 
dataDiskSize, null,
-                            hypervisorType, HTTPMethod.GET, userData, null, 
null, sshKeyPairs,
+                            hypervisorType, HTTPMethod.GET, userData, 
userDataId, userDataDetails, sshKeyPairs,
                             null, null, true, null, affinityGroupIdList, 
customParameters, null, null, null,
                             null, true, overrideDiskOfferingId, null);
                 } else {
                     vm = userVmService.createAdvancedVirtualMachine(zone, 
serviceOffering, template, networkIds, owner, vmHostName, vmHostName,
                             diskOfferingId, dataDiskSize, null,
-                            hypervisorType, HTTPMethod.GET, userData, null, 
null, sshKeyPairs,
+                            hypervisorType, HTTPMethod.GET, userData, 
userDataId, userDataDetails, sshKeyPairs,
                             null, addrs, true, null, affinityGroupIdList, 
customParameters, null, null, null,
                             null, true, null, overrideDiskOfferingId);
                 }
diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java 
b/server/src/main/java/com/cloud/vm/UserVmManager.java
index 39f1e5d2d28..4f1396913cc 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManager.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManager.java
@@ -92,6 +92,10 @@ public interface UserVmManager extends UserVmService {
 
     void removeInstanceFromInstanceGroup(long vmId);
 
+    String finalizeUserData(String userData, Long userDataId, 
VirtualMachineTemplate template);
+
+    String validateUserData(String userData, HTTPMethod httpmethod);
+
     boolean isVMUsingLocalStorage(VMInstanceVO vm);
 
     boolean expunge(UserVmVO vm);
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index ecf60556db6..94ceb0de363 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -4769,7 +4769,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         }
     }
 
-    protected String validateUserData(String userData, HTTPMethod httpmethod) {
+    @Override
+    public String validateUserData(String userData, HTTPMethod httpmethod) {
         byte[] decodedUserData = null;
         if (userData != null) {
 
@@ -5703,7 +5704,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         return userVm.getHypervisorType();
     }
 
-    protected String finalizeUserData(String userData, Long userDataId, 
VirtualMachineTemplate template) {
+    @Override
+    public String finalizeUserData(String userData, Long userDataId, 
VirtualMachineTemplate template) {
         if (StringUtils.isEmpty(userData) && userDataId == null && (template 
== null || template.getUserDataId() == null)) {
             return null;
         }
diff --git a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java 
b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java
index f7cf7f58b2c..fff6fb2a950 100644
--- a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java
+++ b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java
@@ -32,6 +32,7 @@ import java.util.UUID;
 
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
+import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
 import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
 import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
 import org.apache.cloudstack.api.response.UsageRecordResponse;
@@ -52,17 +53,22 @@ import org.powermock.modules.junit4.PowerMockRunner;
 import com.cloud.domain.DomainVO;
 import com.cloud.network.as.AutoScaleVmGroup;
 import com.cloud.network.as.AutoScaleVmGroupVO;
+import com.cloud.network.as.AutoScaleVmProfileVO;
 import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.LoadBalancerVO;
 import com.cloud.network.dao.NetworkServiceMapDao;
 import com.cloud.network.dao.NetworkVO;
+import com.cloud.storage.VMTemplateVO;
 import com.cloud.usage.UsageVO;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.AccountVO;
 import com.cloud.user.User;
+import com.cloud.user.UserData;
+import com.cloud.user.UserDataVO;
 import com.cloud.user.UserVO;
+import com.cloud.user.dao.UserDataDao;
 import com.cloud.utils.net.Ip;
 import com.cloud.vm.NicSecondaryIp;
 
@@ -86,12 +92,27 @@ public class ApiResponseHelperTest {
     @Mock
     AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDaoMock;
 
+    @Mock
+    UserDataDao userDataDaoMock;
+
     @Spy
     @InjectMocks
     ApiResponseHelper apiResponseHelper = new ApiResponseHelper();
 
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss 
ZZZ");
 
+    static long zoneId = 1L;
+    static long domainId = 2L;
+    static long accountId = 3L;
+    static long serviceOfferingId = 4L;
+    static long templateId  = 5L;
+    static String userdata = "userdata";
+    static long userdataId = 6L;
+    static String userdataDetails = "userdataDetails";
+    static String userdataNew = "userdataNew";
+
+    static long autoScaleUserId = 7L;
+
     @Before
     public void injectMocks() throws SecurityException, NoSuchFieldException,
             IllegalArgumentException, IllegalAccessException {
@@ -297,4 +318,55 @@ public class ApiResponseHelperTest {
         assertEquals("8080", response.getPublicPort());
         assertEquals("8081", response.getPrivatePort());
     }
+
+    @Test
+    @PrepareForTest(ApiDBUtils.class)
+    public void testAutoScaleVmProfileResponse() {
+        AutoScaleVmProfileVO vmProfile = new AutoScaleVmProfileVO(zoneId, 
domainId, accountId, serviceOfferingId, templateId, null, null, userdata, null, 
autoScaleUserId);
+        vmProfile.setUserDataId(userdataId);
+        vmProfile.setUserDataDetails(userdataDetails);
+
+        PowerMockito.mockStatic(ApiDBUtils.class);
+        when(ApiDBUtils.findAccountById(anyLong())).thenReturn(new 
AccountVO());
+        when(ApiDBUtils.findDomainById(anyLong())).thenReturn(new DomainVO());
+
+        UserData.UserDataOverridePolicy templatePolicy = 
UserData.UserDataOverridePolicy.APPEND;
+        VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
+        when(ApiDBUtils.findTemplateById(anyLong())).thenReturn(templateVO);
+        
when(templateVO.getUserDataOverridePolicy()).thenReturn(templatePolicy);
+
+        UserDataVO userDataVO =  Mockito.mock(UserDataVO.class);
+        String userDataUuid = "userDataUuid";
+        String userDataName = "userDataName";
+        when(userDataDaoMock.findById(anyLong())).thenReturn(userDataVO);
+        when(userDataVO.getUuid()).thenReturn(userDataUuid);
+        when(userDataVO.getName()).thenReturn(userDataName);
+
+        AutoScaleVmProfileResponse response = 
apiResponseHelper.createAutoScaleVmProfileResponse(vmProfile);
+        assertEquals(templatePolicy.toString(), response.getUserDataPolicy());
+        assertEquals(userdata, response.getUserData());
+        assertEquals(userDataUuid, response.getUserDataId());
+        assertEquals(userDataName, response.getUserDataName());
+        assertEquals(userdataDetails, response.getUserDataDetails());
+    }
+
+    @Test
+    @PrepareForTest(ApiDBUtils.class)
+    public void testAutoScaleVmProfileResponseWithoutUserData() {
+        AutoScaleVmProfileVO vmProfile = new AutoScaleVmProfileVO(zoneId, 
domainId, accountId, serviceOfferingId, templateId, null, null, null, null, 
autoScaleUserId);
+
+        PowerMockito.mockStatic(ApiDBUtils.class);
+        when(ApiDBUtils.findAccountById(anyLong())).thenReturn(new 
AccountVO());
+        when(ApiDBUtils.findDomainById(anyLong())).thenReturn(new DomainVO());
+
+        VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
+        when(ApiDBUtils.findTemplateById(anyLong())).thenReturn(templateVO);
+
+        AutoScaleVmProfileResponse response = 
apiResponseHelper.createAutoScaleVmProfileResponse(vmProfile);
+        assertNull(response.getUserDataPolicy());
+        assertNull(response.getUserData());
+        assertNull(response.getUserDataId());
+        assertNull(response.getUserDataName());
+        assertNull(response.getUserDataDetails());
+    }
 }
diff --git 
a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java 
b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
index e60ce86fc3f..08070faf92c 100644
--- a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
@@ -47,6 +47,7 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd;
 import 
org.apache.cloudstack.api.command.user.autoscale.CreateAutoScalePolicyCmd;
 import 
org.apache.cloudstack.api.command.user.autoscale.CreateAutoScaleVmGroupCmd;
@@ -340,6 +341,10 @@ public class AutoScaleManagerImplTest {
     private static final Long scaleDownCounterId = 38L;
     private static final Long nextVmSeq = 39L;
     private static final Long networkOfferingId = 40L;
+    private static final String userData = "VGVzdFVzZXJEYXRh";  //TestUserData
+    private static final Long userDataId = 41L;
+    private static final Map<String, HashMap<String, String>> userDataDetails 
= new HashMap<>();
+    private static final String userDataFinal = "VGVzdFVzZXJEYXRhRmluYWw=";  
//TestUserDataFinal
 
     @Mock
     DataCenterVO zoneMock;
@@ -404,6 +409,10 @@ public class AutoScaleManagerImplTest {
         
Mockito.doNothing().when(accountManager).checkAccess(Mockito.any(Account.class),
 Mockito.isNull(), Mockito.anyBoolean(), Mockito.any());
 
         
when(asPolicyDao.persist(any(AutoScalePolicyVO.class))).thenReturn(asScaleUpPolicyMock);
+
+        userDataDetails.put("0", new HashMap<>() {{ put("key1", "value1"); 
put("key2", "value2"); }});
+        
Mockito.doReturn(userDataFinal).when(userVmMgr).finalizeUserData(any(), any(), 
any());
+        
Mockito.doReturn(userDataFinal).when(userVmMgr).validateUserData(eq(userDataFinal),
 nullable(BaseCmd.HTTPMethod.class));
     }
 
     @After
@@ -748,10 +757,48 @@ public class AutoScaleManagerImplTest {
         ReflectionTestUtils.setField(cmd, "otherDeployParams", 
otherDeployParams);
         ReflectionTestUtils.setField(cmd, "counterParamList", 
counterParamList);
 
+        ReflectionTestUtils.setField(cmd, "userData", userData);
+        ReflectionTestUtils.setField(cmd, "userDataId", userDataId);
+        ReflectionTestUtils.setField(cmd, "userDataDetails", userDataDetails);
+
         AutoScaleVmProfile vmProfile = 
autoScaleManagerImplSpy.createAutoScaleVmProfile(cmd);
 
         Assert.assertEquals(asVmProfileMock, vmProfile);
         Mockito.verify(autoScaleVmProfileDao).persist(Mockito.any());
+
+        Mockito.verify(userVmMgr).finalizeUserData(any(), any(), any());
+        Mockito.verify(userVmMgr).validateUserData(eq(userDataFinal), 
nullable(BaseCmd.HTTPMethod.class));
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    @PrepareForTest(ComponentContext.class)
+    public void testCreateAutoScaleVmProfileFail() {
+        when(entityManager.findById(DataCenter.class, 
zoneId)).thenReturn(zoneMock);
+        when(entityManager.findById(ServiceOffering.class, 
serviceOfferingId)).thenReturn(serviceOfferingMock);
+        when(entityManager.findByIdIncludingRemoved(ServiceOffering.class, 
serviceOfferingId)).thenReturn(serviceOfferingMock);
+        when(entityManager.findById(VirtualMachineTemplate.class, 
templateId)).thenReturn(templateMock);
+        when(serviceOfferingMock.isDynamic()).thenReturn(false);
+        
Mockito.doThrow(InvalidParameterValueException.class).when(userVmMgr).finalizeUserData(any(),
 any(), any());
+
+        DispatchChain dispatchChainMock = Mockito.mock(DispatchChain.class);
+        
when(dispatchChainFactory.getStandardDispatchChain()).thenReturn(dispatchChainMock);
+        Mockito.doNothing().when(dispatchChainMock).dispatch(any());
+        PowerMockito.mockStatic(ComponentContext.class);
+        
when(ComponentContext.inject(DeployVMCmd.class)).thenReturn(Mockito.mock(DeployVMCmd.class));
+
+        CreateAutoScaleVmProfileCmd cmd = new CreateAutoScaleVmProfileCmd();
+
+        ReflectionTestUtils.setField(cmd, "zoneId", zoneId);
+        ReflectionTestUtils.setField(cmd, "serviceOfferingId", 
serviceOfferingId);
+        ReflectionTestUtils.setField(cmd, "templateId", templateId);
+        ReflectionTestUtils.setField(cmd, "expungeVmGracePeriod", 
expungeVmGracePeriod);
+        ReflectionTestUtils.setField(cmd, "otherDeployParams", 
otherDeployParams);
+        ReflectionTestUtils.setField(cmd, "counterParamList", 
counterParamList);
+
+        ReflectionTestUtils.setField(cmd, "userData", userData);
+        ReflectionTestUtils.setField(cmd, "userDataId", userDataId);
+
+        AutoScaleVmProfile vmProfile = 
autoScaleManagerImplSpy.createAutoScaleVmProfile(cmd);
     }
 
     @Test
@@ -774,10 +821,17 @@ public class AutoScaleManagerImplTest {
         ReflectionTestUtils.setField(cmd, "serviceOfferingId", 
serviceOfferingId);
         ReflectionTestUtils.setField(cmd, "templateId", templateId);
 
+        ReflectionTestUtils.setField(cmd, "userData", userData);
+        ReflectionTestUtils.setField(cmd, "userDataId", userDataId);
+        ReflectionTestUtils.setField(cmd, "userDataDetails", userDataDetails);
+
         AutoScaleVmProfile vmProfile = 
autoScaleManagerImplSpy.updateAutoScaleVmProfile(cmd);
 
         Assert.assertEquals(asVmProfileMock, vmProfile);
         Mockito.verify(autoScaleVmProfileDao).persist(Mockito.any());
+
+        Mockito.verify(userVmMgr).finalizeUserData(any(), any(), any());
+        Mockito.verify(userVmMgr).validateUserData(eq(userDataFinal), 
nullable(BaseCmd.HTTPMethod.class));
     }
 
     @Test
@@ -1208,6 +1262,9 @@ public class AutoScaleManagerImplTest {
         when(asVmProfileMock.getAccountId()).thenReturn(accountId);
         when(asVmProfileMock.getZoneId()).thenReturn(zoneId);
         when(asVmProfileMock.getOtherDeployParams()).thenReturn("");
+        when(asVmProfileMock.getUserData()).thenReturn(userData);
+        when(asVmProfileMock.getUserDataId()).thenReturn(userDataId);
+        
when(asVmProfileMock.getUserDataDetails()).thenReturn(userDataDetails.toString());
 
         
when(accountService.getActiveAccountById(accountId)).thenReturn(account);
         when(entityManager.findById(DataCenter.class, 
zoneId)).thenReturn(zoneMock);
@@ -1224,7 +1281,7 @@ public class AutoScaleManagerImplTest {
         when(userVmMock.getId()).thenReturn(virtualMachineId);
         
when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
         when(userVmService.createBasicSecurityGroupVirtualMachine(any(), 
any(), any(), any(), any(), any(), any(),
-                any(), any(), any(), any(), any(), any(), any(), any(), any(), 
any(), any(), eq(true), any(), any(), any(),
+                any(), any(), any(), any(), any(), eq(userData), 
eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), 
any(), any(), any(),
                 any(), any(), any(), any(), eq(true), 
any())).thenReturn(userVmMock);
 
         long result = autoScaleManagerImplSpy.createNewVM(asVmGroupMock);
@@ -1235,7 +1292,7 @@ public class AutoScaleManagerImplTest {
                 "-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
         
Mockito.verify(userVmService).createBasicSecurityGroupVirtualMachine(any(), 
any(), any(), any(), any(),
                 matches(vmHostNamePattern), matches(vmHostNamePattern),
-                any(), any(), any(), any(), any(), any(), any(), any(), any(), 
any(), any(), eq(true), any(), any(), any(),
+                any(), any(), any(), any(), any(), eq(userData), 
eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), 
any(), any(), any(),
                 any(), any(), any(), any(), eq(true), any());
         Mockito.verify(asVmGroupMock).setNextVmSeq(nextVmSeq + 1);
     }
@@ -1253,6 +1310,9 @@ public class AutoScaleManagerImplTest {
         when(asVmProfileMock.getAccountId()).thenReturn(accountId);
         when(asVmProfileMock.getZoneId()).thenReturn(zoneId);
         when(asVmProfileMock.getOtherDeployParams()).thenReturn("");
+        when(asVmProfileMock.getUserData()).thenReturn(userData);
+        when(asVmProfileMock.getUserDataId()).thenReturn(userDataId);
+        
when(asVmProfileMock.getUserDataDetails()).thenReturn(userDataDetails.toString());
 
         
when(accountService.getActiveAccountById(accountId)).thenReturn(account);
         when(entityManager.findById(DataCenter.class, 
zoneId)).thenReturn(zoneMock);
@@ -1270,7 +1330,7 @@ public class AutoScaleManagerImplTest {
         
when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
         when(zoneMock.isSecurityGroupEnabled()).thenReturn(true);
         when(userVmService.createAdvancedSecurityGroupVirtualMachine(any(), 
any(), any(), any(), any(), any(), any(),
-                any(), any(), any(), any(), any(), any(), any(), any(), any(), 
any(), any(), any(), any(), any(), any(),
+                any(), any(), any(), any(), any(), any(), eq(userData), 
eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), any(), 
any(), any(),
                 any(), any(), any(), any(), any(), eq(true), any(), 
any())).thenReturn(userVmMock);
 
         long result = autoScaleManagerImplSpy.createNewVM(asVmGroupMock);
@@ -1281,7 +1341,7 @@ public class AutoScaleManagerImplTest {
                 "-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
         
Mockito.verify(userVmService).createAdvancedSecurityGroupVirtualMachine(any(), 
any(), any(), any(), any(), any(),
                 matches(vmHostNamePattern), matches(vmHostNamePattern),
-                any(), any(), any(), any(), any(), any(), any(), any(), any(), 
any(), any(), any(), any(), any(),
+                any(), any(), any(), any(), any(), eq(userData), 
eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), any(), 
any(), any(),
                 any(), any(), any(), any(), any(), eq(true), any(), any());
         Mockito.verify(asVmGroupMock).setNextVmSeq(nextVmSeq + 2);
     }
@@ -1299,6 +1359,9 @@ public class AutoScaleManagerImplTest {
         when(asVmProfileMock.getAccountId()).thenReturn(accountId);
         when(asVmProfileMock.getZoneId()).thenReturn(zoneId);
         when(asVmProfileMock.getOtherDeployParams()).thenReturn("");
+        when(asVmProfileMock.getUserData()).thenReturn(userData);
+        when(asVmProfileMock.getUserDataId()).thenReturn(userDataId);
+        
when(asVmProfileMock.getUserDataDetails()).thenReturn(userDataDetails.toString());
 
         
when(accountService.getActiveAccountById(accountId)).thenReturn(account);
         when(entityManager.findById(DataCenter.class, 
zoneId)).thenReturn(zoneMock);
@@ -1316,7 +1379,7 @@ public class AutoScaleManagerImplTest {
         
when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
         when(zoneMock.isSecurityGroupEnabled()).thenReturn(false);
         when(userVmService.createAdvancedVirtualMachine(any(), any(), any(), 
any(), any(), any(), any(),
-                any(), any(), any(), any(), any(), any(), any(), any(), any(), 
any(), any(), eq(true), any(), any(), any(),
+                any(), any(), any(), any(), any(), eq(userData), 
eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), 
any(), any(), any(),
                 any(), any(), any(), any(), eq(true), any(), 
any())).thenReturn(userVmMock);
 
         long result = autoScaleManagerImplSpy.createNewVM(asVmGroupMock);
@@ -1327,7 +1390,7 @@ public class AutoScaleManagerImplTest {
                 "-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
         Mockito.verify(userVmService).createAdvancedVirtualMachine(any(), 
any(), any(), any(), any(),
                 matches(vmHostNamePattern), matches(vmHostNamePattern),
-                any(), any(), any(), any(), any(), any(), any(), any(), any(), 
any(), any(), eq(true), any(), any(), any(),
+                any(), any(), any(), any(), any(), eq(userData), 
eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), 
any(), any(), any(),
                 any(), any(), any(), any(), eq(true), any(), any());
         Mockito.verify(asVmGroupMock).setNextVmSeq(nextVmSeq + 3);
     }
diff --git a/test/integration/smoke/test_vm_autoscaling.py 
b/test/integration/smoke/test_vm_autoscaling.py
index d9fa7e23b79..316f94fd5fd 100644
--- a/test/integration/smoke/test_vm_autoscaling.py
+++ b/test/integration/smoke/test_vm_autoscaling.py
@@ -38,6 +38,7 @@ from marvin.lib.base import (Account,
                              Domain,
                              Project,
                              ServiceOffering,
+                             Template,
                              VirtualMachine,
                              Volume,
                              Zone,
@@ -47,6 +48,7 @@ from marvin.lib.base import (Account,
                              LoadBalancerRule,
                              VPC,
                              VpcOffering,
+                             UserData,
                              SSHKeyPair)
 
 from marvin.lib.common import (get_domain,
@@ -198,6 +200,37 @@ class TestVmAutoScaling(cloudstackTestCase):
             name="keypair2"
         )
 
+        # 8-2. Register userdata
+        cls.apiUserdata = UserData.register(
+            cls.apiclient,
+            name="ApiUserdata",
+            userdata="QVBJdXNlcmRhdGE=", #APIuserdata
+            account=cls.regular_user.name,
+            domainid=cls.regular_user.domainid
+        )
+
+        # 8-3. Register userdata for template
+        cls.templateUserdata = UserData.register(
+            cls.apiclient,
+            name="TemplateUserdata",
+            
userdata="IyMgdGVtcGxhdGU6IGppbmphCiNjbG91ZC1jb25maWcKcnVuY21kOgogICAgLSBlY2hvICdrZXkge3sgZHMubWV0YV9kYXRhLmtleTEgfX0nID4+IC9yb290L2luc3RhbmNlX21ldGFkYXRhCgo=",
+            #    ## template: jinja
+            #    #cloud-config
+            #    runcmd:
+            #        - echo 'key {{ ds.meta_data.key1 }}' >> 
/root/instance_metadata
+            #
+            account=cls.regular_user.name,
+            domainid=cls.regular_user.domainid
+        )
+
+        # 8-3. Link userdata to template
+        cls.template = Template.linkUserDataToTemplate(
+            cls.apiclient,
+            templateid=cls.template.id,
+            userdataid=cls.templateUserdata.userdata.id,
+            userdatapolicy="append"
+        )
+
         # 9. Get counters for cpu and memory
         counters = Autoscale.listCounters(
             cls.regular_user_apiclient,
@@ -294,6 +327,7 @@ class TestVmAutoScaling(cloudstackTestCase):
             serviceofferingid=cls.service_offering.id,
             zoneid=cls.zone.id,
             templateid=cls.template.id,
+            userdata="VGVzdFVzZXJEYXRh", #TestUserData
             expungevmgraceperiod=DEFAULT_EXPUNGE_VM_GRACE_PERIOD,
             otherdeployparams=cls.otherdeployparams
         )
@@ -349,6 +383,10 @@ class TestVmAutoScaling(cloudstackTestCase):
 
     @classmethod
     def tearDownClass(cls):
+        cls.template = Template.linkUserDataToTemplate(
+            cls.apiclient,
+            templateid=cls.template.id
+        )
         Configurations.update(cls.apiclient,
                               CONFIG_NAME_DISK_CONTROLLER,
                               cls.initial_vmware_root_disk_controller)
@@ -390,6 +428,7 @@ class TestVmAutoScaling(cloudstackTestCase):
             self.regular_user_apiclient,
             autoscalevmgroupid=autoscalevmgroupid,
             projectid=projectid,
+            userdata=True,
             listall=True
         )
         self.assertEqual(
@@ -505,6 +544,31 @@ class TestVmAutoScaling(cloudstackTestCase):
         else:
             self.assertEquals(affinitygroupids, '')
 
+        userdata = None
+        userdatadetails = None
+        userdataid = None
+        if vm.userdata:
+            userdata = vm.userdata
+        if vm.userdatadetails:
+            userdatadetails = vm.userdatadetails
+        if vm.userdataid:
+            userdataid = vm.userdataid
+
+        if vmprofile.userdataid:
+            self.assertEquals(userdataid, vmprofile.userdataid)
+        else:
+            self.assertIsNone(userdataid)
+
+        if vmprofile.userdatadetails:
+            self.assertEquals(userdatadetails, vmprofile.userdatadetails)
+        else:
+            self.assertIsNone(userdatadetails)
+
+        if vmprofile.userdata:
+            self.assertEquals(userdata, vmprofile.userdata)
+        else:
+            self.assertIsNone(userdata)
+
     def wait_for_vm_start(self, vm=None, project_id=None):
         """ Wait until vm is Running """
         def check_user_vm_state():
@@ -512,6 +576,7 @@ class TestVmAutoScaling(cloudstackTestCase):
                 self.apiclient,
                 id=vm.id,
                 projectid=project_id,
+                userdata=True,
                 listall=True
             )
             if isinstance(vms, list):
@@ -576,6 +641,8 @@ class TestVmAutoScaling(cloudstackTestCase):
             Autoscale.updateAutoscaleVMProfile(
                 self.regular_user_apiclient,
                 id = self.autoscaling_vmprofile.id,
+                userdataid=self.apiUserdata.userdata.id,
+                userdatadetails=[{"key1": "value2"}],
                 serviceofferingid = self.service_offering_new.id,
                 expungevmgraceperiod = DEFAULT_EXPUNGE_VM_GRACE_PERIOD + 1,
                 otherdeployparams = otherdeployparams_new
@@ -712,6 +779,7 @@ class TestVmAutoScaling(cloudstackTestCase):
         vms = VirtualMachine.list(
             self.regular_user_apiclient,
             autoscalevmgroupid=self.autoscaling_vmgroup.id,
+            userdata=True,
             listall=True
         )
         self.assertEqual(
@@ -889,6 +957,7 @@ class TestVmAutoScaling(cloudstackTestCase):
             self.regular_user_apiclient,
             autoscalevmgroupid=autoscaling_vmgroup_project.id,
             projectid=project.id,
+            userdata=True,
             listall=True
         )
         self.assertEqual(
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index f93cbd233f7..dcdd0bf97c1 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1625,6 +1625,7 @@
 "label.reset.config.value": "Reset to default value",
 "label.reset.ssh.key.pair": "Reset SSH key pair",
 "label.reset.to.default": "Reset to default",
+"label.reset.userdata.on.autoscale.vm.group": "Reset Userdata on AutoScale VM 
Group",
 "label.reset.userdata.on.vm": "Reset Userdata on VM",
 "label.reset.vpn.connection": "Reset VPN connection",
 "label.resource": "Resource",
diff --git a/ui/src/views/compute/AutoScaleVmProfile.vue 
b/ui/src/views/compute/AutoScaleVmProfile.vue
index 45947753b06..12081dfad72 100644
--- a/ui/src/views/compute/AutoScaleVmProfile.vue
+++ b/ui/src/views/compute/AutoScaleVmProfile.vue
@@ -69,6 +69,47 @@
           {{ getServiceOfferingName(serviceofferingid) }}
         </div>
       </div>
+      <div class="form" v-if="userdataid">
+        <div class="form__item">
+          <div class="form__label">
+            <tooltip-label :title="$t('label.userdataid')"/>
+          </div>
+          {{ userdataid }}
+        </div>
+      </div>
+      <div class="form" v-if="userdataname">
+        <div class="form__item">
+          <div class="form__label">
+            <tooltip-label :title="$t('label.userdataname')"/>
+          </div>
+          {{ userdataname }}
+        </div>
+      </div>
+      <div class="form" v-if="userdatadetails">
+        <div class="form__item">
+          <div class="form__label">
+            <tooltip-label :title="$t('label.userdatadetails')"/>
+          </div>
+          {{ userdatadetails }}
+        </div>
+      </div>
+      <div class="form" v-if="userdatapolicy">
+        <div class="form__item">
+          <div class="form__label">
+            <tooltip-label :title="$t('label.userdatapolicy')"/>
+          </div>
+          {{ userdatapolicy }}
+        </div>
+      </div>
+      <div class="form">
+        <div class="form__item">
+          <div class="form__label">
+            <tooltip-label :title="$t('label.userdata')" 
:tooltip="createAutoScaleVmProfileApiParams.userdata.description"/>
+          </div>
+          <a-textarea v-model:value="userdata" rows="5" :disabled="true">
+          </a-textarea>
+        </div>
+      </div>
       <div class="form">
         <div class="form__item">
           <a-button ref="submit" :disabled="!('updateAutoScaleVmProfile' in 
$store.getters.apis) || resource.state !== 'DISABLED'" type="primary" 
@click="editProfileModalVisible = true">
@@ -76,6 +117,12 @@
             {{ $t('label.edit.autoscale.vmprofile') }}
           </a-button>
         </div>
+        <div class="form__item">
+          <a-button ref="submit" :disabled="!('updateAutoScaleVmProfile' in 
$store.getters.apis) || resource.state !== 'DISABLED'" type="primary" 
@click="showUpdateUserDataForm = true">
+            <template #icon><solution-outlined /></template>
+            {{ $t('label.reset.userdata.on.autoscale.vm.group') }}
+          </a-button>
+        </div>
       </div>
 
       <a-divider/>
@@ -224,20 +271,26 @@
           </a-select>
         </div>
       </div>
-      <div class="form">
-        <div class="form__item">
-          <div class="form__label">
-            <tooltip-label :title="$t('label.userdata')" 
:tooltip="createAutoScaleVmProfileApiParams.userdata.description"/>
-          </div>
-          <a-textarea v-model:value="userdata">
-          </a-textarea>
-        </div>
-      </div>
       <div :span="24" class="action-button">
         <a-button :loading="loading" @click="closeModal">{{ $t('label.cancel') 
}}</a-button>
         <a-button :loading="loading" ref="submit" type="primary" 
@click="updateAutoScaleVmProfile">{{ $t('label.ok') }}</a-button>
       </div>
     </a-modal>
+
+    <a-modal
+      :visible="showUpdateUserDataForm"
+      :title="$t('label.reset.userdata.on.autoscale.vm.group')"
+      :closable="true"
+      :maskClosable="false"
+      :footer="null"
+      @cancel="showUpdateUserDataForm = false"
+      centered
+      width="auto">
+      <reset-user-data
+        :resource="{ ...resource, ...{ resetUserDataApiName: 
'updateAutoScaleVmProfile', resetUserDataResourceId: this.resource.vmprofileid, 
templateid: this.templateid}}"
+        @close-action="showUpdateUserDataForm = false"
+      />
+    </a-modal>
   </div>
 </template>
 
@@ -247,10 +300,12 @@ import { isAdmin, isAdminOrDomainAdmin } from '@/role'
 import Status from '@/components/widgets/Status'
 import TooltipButton from '@/components/widgets/TooltipButton'
 import TooltipLabel from '@/components/widgets/TooltipLabel'
+import ResetUserData from '@views/compute/ResetUserData'
 
 export default {
   name: 'conditionsTab',
   components: {
+    ResetUserData,
     Status,
     TooltipButton,
     TooltipLabel
@@ -266,12 +321,17 @@ export default {
       filterColumns: ['Action'],
       loading: true,
       editProfileModalVisible: false,
+      showUpdateUserDataForm: false,
       profileid: null,
       autoscaleuserid: null,
       expungevmgraceperiod: null,
       templateid: null,
       serviceofferingid: null,
       userdata: null,
+      userdataid: null,
+      userdataname: null,
+      userdatadetails: null,
+      userdatapolicy: null,
       usersList: [],
       templatesList: [],
       serviceOfferingsList: [],
@@ -384,6 +444,11 @@ export default {
         this.serviceofferingid = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.serviceofferingid
         this.templateid = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.templateid
         this.userdata = 
this.decodeUserData(decodeURIComponent(response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.userdata
 || ''))
+        this.userdataid = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.userdataid
+        this.userdataname = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.userdataname
+        this.userdatadetails = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.userdatadetails
+        this.userdatapolicy = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.userdatapolicy
+
         const counterparam = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.counterparam 
|| {}
         const otherdeployparams = 
response.listautoscalevmprofilesresponse?.autoscalevmprofile?.[0]?.otherdeployparams
 || {}
         this.finalizeParams(counterparam, otherdeployparams)
@@ -518,13 +583,10 @@ export default {
       if (this.autoscaleuserid) {
         params.autoscaleuserid = this.autoscaleuserid
       }
-      if (this.userdata && this.userdata.length > 0) {
-        params.userdata = this.$toBase64AndURIEncoded(this.userdata)
-      }
 
-      const httpMethod = params.userdata ? 'POST' : 'GET'
-      const args = httpMethod === 'POST' ? {} : params
-      const data = httpMethod === 'POST' ? params : {}
+      const httpMethod = 'GET'
+      const args = params
+      const data = {}
 
       api('updateAutoScaleVmProfile', args, httpMethod, data).then(response => 
{
         this.$pollJob({
diff --git a/ui/src/views/compute/CreateAutoScaleVmGroup.vue 
b/ui/src/views/compute/CreateAutoScaleVmGroup.vue
index b764f8185e2..81da8a9b6d7 100644
--- a/ui/src/views/compute/CreateAutoScaleVmGroup.vue
+++ b/ui/src/views/compute/CreateAutoScaleVmGroup.vue
@@ -407,7 +407,7 @@
 
                         <span v-if="property.type && 
property.type==='boolean'">
                           <a-switch
-                            v-model:cheked="form['properties.' + 
escapePropertyKey(property.key)]"
+                            v-model:checked="form['properties.' + 
escapePropertyKey(property.key)]"
                             :placeholder="property.description"
                           />
                         </span>
@@ -760,13 +760,100 @@
                         @select-affinity-group-item="($event) => 
updateAffinityGroups($event)"
                         @handle-search-filter="($event) => 
handleSearchFilter('affinityGroups', $event)"/>
                     </a-form-item>
-                    <a-form-item name="userdata" ref="userdata">
+                    <a-form-item>
                       <template #label>
                         <tooltip-label :title="$t('label.userdata')" 
:tooltip="createAutoScaleVmProfileApiParams.userdata.description"/>
                       </template>
-                      <a-textarea
-                        v-model:value="form.userdata">
-                      </a-textarea>
+                      <a-card>
+                        <div v-if="this.template && this.template.userdataid">
+                          <a-text type="primary">
+                            Userdata "{{ $t(this.template.userdataname) }}" is 
linked with template "{{ $t(this.template.name) }}" with override policy "{{ 
$t(this.template.userdatapolicy) }}"
+                          </a-text><br/><br/>
+                          <div v-if="templateUserDataParams.length > 0 && 
!doUserdataOverride">
+                            <a-text type="primary" v-if="this.template && 
this.template.userdataid && templateUserDataParams.length > 0">
+                              Enter the values for the variables in userdata
+                            </a-text>
+                            <a-input-group>
+                              <a-table
+                                size="small"
+                                style="overflow-y: auto"
+                                :columns="userDataParamCols"
+                                :dataSource="templateUserDataParams"
+                                :pagination="false"
+                                :rowKey="record => record.key">
+                                <template #value="{ record }">
+                                  <a-input 
v-model:value="templateUserDataValues[record.key]" />
+                                </template>
+                              </a-table>
+                            </a-input-group>
+                          </div>
+                        </div><br/><br/>
+                        <div v-if="userdataDefaultOverridePolicy === 
'ALLOWOVERRIDE' || userdataDefaultOverridePolicy === 'APPEND' || 
!userdataDefaultOverridePolicy">
+                          <span v-if="userdataDefaultOverridePolicy === 
'ALLOWOVERRIDE'" >
+                            {{ $t('label.userdata.do.override') }}
+                            <a-switch v-model:checked="doUserdataOverride" 
style="margin-left: 10px"/>
+                          </span>
+                          <span v-if="userdataDefaultOverridePolicy === 
'APPEND'">
+                            {{ $t('label.userdata.do.append') }}
+                            <a-switch v-model:checked="doUserdataAppend" 
style="margin-left: 10px"/>
+                          </span>
+                          <a-step
+                            :status="zoneSelected ? 'process' : 'wait'">
+                            <template #description>
+                              <div v-if="doUserdataOverride || 
doUserdataAppend || !userdataDefaultOverridePolicy" style="margin-top: 15px">
+                                <a-card
+                                  :tabList="userdataTabList"
+                                  :activeTabKey="userdataTabKey"
+                                  @tabChange="key => onUserdataTabChange(key, 
'userdataTabKey')">
+                                  <div v-if="userdataTabKey === 
'userdataregistered'">
+                                    <a-step
+                                      v-if="isUserAllowedToListUserDatas"
+                                      :status="zoneSelected ? 'process' : 
'wait'">
+                                      <template #description>
+                                        <div v-if="zoneSelected">
+                                          <user-data-selection
+                                            :items="options.userDatas"
+                                            :row-count="rowCount.userDatas"
+                                            :zoneId="zoneId"
+                                            :disabled="template.userdatapolicy 
=== 'DENYOVERRIDE'"
+                                            :loading="loading.userDatas"
+                                            :preFillContent="dataPreFill"
+                                            @select-user-data-item="($event) 
=> updateUserData($event)"
+                                            @handle-search-filter="($event) => 
handleSearchFilter('userData', $event)"
+                                          />
+                                          <div v-if="userDataParams.length > 
0">
+                                            <a-input-group>
+                                              <a-table
+                                                size="small"
+                                                style="overflow-y: auto"
+                                                :columns="userDataParamCols"
+                                                :dataSource="userDataParams"
+                                                :pagination="false"
+                                                :rowKey="record => record.key">
+                                                <template #value="{ record }">
+                                                  <a-input 
v-model:value="userDataValues[record.key]" />
+                                                </template>
+                                              </a-table>
+                                            </a-input-group>
+                                          </div>
+                                        </div>
+                                      </template>
+                                    </a-step>
+                                  </div>
+                                  <div v-else>
+                                    <a-form-item name="userdata" 
ref="userdata" >
+                                      <a-textarea
+                                        placeholder="Userdata"
+                                        v-model:value="form.userdata">
+                                      </a-textarea>
+                                    </a-form-item>
+                                  </div>
+                                </a-card>
+                              </div>
+                            </template>
+                          </a-step>
+                        </div>
+                      </a-card>
                     </a-form-item>
                   </div>
                 </template>
@@ -952,6 +1039,7 @@ import NetworkSelection from 
'@views/compute/wizard/NetworkSelection'
 import NetworkConfiguration from '@views/compute/wizard/NetworkConfiguration'
 import LoadBalancerSelection from '@views/compute/wizard/LoadBalancerSelection'
 import SshKeyPairSelection from '@views/compute/wizard/SshKeyPairSelection'
+import UserDataSelection from '@views/compute/wizard/UserDataSelection'
 import SecurityGroupSelection from 
'@views/compute/wizard/SecurityGroupSelection'
 import TooltipLabel from '@/components/widgets/TooltipLabel'
 import InstanceNicsNetworkSelectListView from 
'@/components/view/InstanceNicsNetworkSelectListView.vue'
@@ -964,6 +1052,7 @@ export default {
   name: 'Wizard',
   components: {
     SshKeyPairSelection,
+    UserDataSelection,
     NetworkConfiguration,
     NetworkSelection,
     LoadBalancerSelection,
@@ -1010,6 +1099,10 @@ export default {
       zoneSelected: false,
       dynamicscalingenabled: true,
       templateKey: 0,
+      showRegisteredUserdata: true,
+      doUserdataOverride: false,
+      doUserdataAppend: false,
+      userdataDefaultOverridePolicy: 'ALLOWOVERRIDE',
       vm: {
         name: null,
         zoneid: null,
@@ -1034,6 +1127,7 @@ export default {
         affinityGroups: [],
         networks: [],
         sshKeyPairs: [],
+        UserDatas: [],
         loadbalancers: []
       },
       rowCount: {},
@@ -1045,6 +1139,7 @@ export default {
         affinityGroups: false,
         networks: false,
         sshKeyPairs: false,
+        userDatas: false,
         loadbalancers: false,
         zones: false
       },
@@ -1123,6 +1218,32 @@ export default {
       zone: {},
       sshKeyPairs: [],
       sshKeyPair: {},
+      userData: {},
+      userDataParams: [],
+      userDataParamCols: [
+        {
+          title: this.$t('label.key'),
+          dataIndex: 'key'
+        },
+        {
+          title: this.$t('label.value'),
+          dataIndex: 'value',
+          slots: { customRender: 'value' }
+        }
+      ],
+      userDataValues: {},
+      templateUserDataCols: [
+        {
+          title: this.$t('label.userdata'),
+          dataIndex: 'userdata'
+        },
+        {
+          title: this.$t('label.userdatapolicy'),
+          dataIndex: 'userdataoverridepolicy'
+        }
+      ],
+      templateUserDataParams: [],
+      templateUserDataValues: {},
       overrideDiskOffering: {},
       templateFilter: [
         'featured',
@@ -1134,6 +1255,7 @@ export default {
       defaultNetworkId: '',
       dataNetworkCreated: [],
       tabKey: 'templateid',
+      userdataTabKey: 'userdataregistered',
       dataPreFill: {},
       showDetails: false,
       showRootDiskSizeChanger: false,
@@ -1224,6 +1346,15 @@ export default {
             listall: false
           }
         },
+        userDatas: {
+          list: 'listUserData',
+          options: {
+            page: 1,
+            pageSize: 10,
+            keyword: undefined,
+            listall: false
+          }
+        },
         networks: {
           list: 'listNetworks',
           options: {
@@ -1285,12 +1416,27 @@ export default {
       }]
       return tabList
     },
+    userdataTabList () {
+      let tabList = []
+      tabList = [{
+        key: 'userdataregistered',
+        tab: this.$t('label.userdata.registered')
+      },
+      {
+        key: 'userdatatext',
+        tab: this.$t('label.userdata.text')
+      }]
+      return tabList
+    },
     showSecurityGroupSection () {
       return (this.networks.length > 0 && this.zone.securitygroupsenabled) || 
(this.zone && this.zone.networktype === 'Basic')
     },
     isUserAllowedToListSshKeys () {
       return Boolean('listSSHKeyPairs' in this.$store.getters.apis)
     },
+    isUserAllowedToListUserDatas () {
+      return Boolean('listUserData' in this.$store.getters.apis)
+    },
     dynamicScalingVmConfigValue () {
       return this.options.dynamicScalingVmConfig?.[0]?.value === 'true'
     },
@@ -1421,6 +1567,8 @@ export default {
   template (oldValue, newValue) {
     if (oldValue && newValue && oldValue.id !== newValue.id) {
       this.dynamicscalingenabled = this.isDynamicallyScalable()
+      this.doUserdataOverride = false
+      this.doUserdataAppend = false
     }
   },
   beforeCreate () {
@@ -1853,6 +2001,8 @@ export default {
         if (template) {
           var size = template.size / (1024 * 1024 * 1024) || 0 // bytes to GB
           this.dataPreFill.minrootdisksize = Math.ceil(size)
+          this.updateTemplateLinkedUserData(this.template.userdataid)
+          this.userdataDefaultOverridePolicy = this.template.userdatapolicy
         }
       } else if (['cpuspeed', 'cpunumber', 'memory'].includes(name)) {
         this.vm[name] = value
@@ -2017,6 +2167,56 @@ export default {
       this.form.keypairs = names
       this.sshKeyPairs = names.map((sshKeyPair) => { return sshKeyPair.name })
     },
+    updateUserData (id) {
+      if (id === '0') {
+        this.form.userdataid = undefined
+        return
+      }
+      this.form.userdataid = id
+      this.userDataParams = []
+      api('listUserData', { id: id }).then(json => {
+        const resp = json?.listuserdataresponse?.userdata || []
+        if (resp) {
+          var params = resp[0].params
+          if (params) {
+            var dataParams = params.split(',')
+          }
+          var that = this
+          dataParams.forEach(function (val, index) {
+            that.userDataParams.push({
+              id: index,
+              key: val
+            })
+          })
+        }
+      })
+    },
+    updateTemplateLinkedUserData (id) {
+      if (id === '0') {
+        return
+      }
+      this.templateUserDataParams = []
+
+      api('listUserData', { id: id }).then(json => {
+        const resp = json?.listuserdataresponse?.userdata || []
+        if (resp) {
+          var params = resp[0].params
+          if (params) {
+            var dataParams = params.split(',')
+          }
+          var that = this
+          that.templateUserDataParams = []
+          if (dataParams) {
+            dataParams.forEach(function (val, index) {
+              that.templateUserDataParams.push({
+                id: index,
+                key: val
+              })
+            })
+          }
+        }
+      })
+    },
     updateAffinityGroups (ids) {
       this.form.affinitygroupids = ids
     },
@@ -2035,16 +2235,20 @@ export default {
         }, 1000)
       })
     },
-    createVmProfile (createVmGroupData) {
+    createVmProfile (createVmGroupData, createVmGroupUserDataDetails) {
       this.addStep('message.creating.autoscale.vmprofile', 'createVmProfile')
 
       return new Promise((resolve, reject) => {
         const params = {
-          expungevmgraceperiod: createVmGroupData.expungevmgraceperiod,
-          serviceofferingid: createVmGroupData.serviceofferingid,
-          templateid: createVmGroupData.templateid,
-          userdata: createVmGroupData.userdata,
-          zoneid: createVmGroupData.zoneid
+          ...createVmGroupUserDataDetails,
+          ...{
+            expungevmgraceperiod: createVmGroupData.expungevmgraceperiod,
+            serviceofferingid: createVmGroupData.serviceofferingid,
+            templateid: createVmGroupData.templateid,
+            userdata: createVmGroupData.userdata,
+            userdataid: createVmGroupData.userdataid,
+            zoneid: createVmGroupData.zoneid
+          }
         }
         if (createVmGroupData.autoscaleuserid) {
           params.autoscaleuserid = createVmGroupData.autoscaleuserid
@@ -2425,9 +2629,13 @@ export default {
         // advanced settings
         createVmGroupData.keypairs = this.sshKeyPairs.join(',')
         createVmGroupData.affinitygroupids = (values.affinitygroupids || 
[]).join(',')
-        if (values.userdata && values.userdata.length > 0) {
+        const isUserdataAllowed = !this.userdataDefaultOverridePolicy || 
(this.userdataDefaultOverridePolicy === 'ALLOWOVERRIDE' && 
this.doUserdataOverride) || (this.userdataDefaultOverridePolicy === 'APPEND' && 
this.doUserdataAppend)
+        if (isUserdataAllowed && values.userdata && values.userdata.length > 
0) {
           createVmGroupData.userdata = 
this.$toBase64AndURIEncoded(values.userdata)
         }
+        if (isUserdataAllowed) {
+          createVmGroupData.userdataid = values.userdataid
+        }
 
         // vm profile details
         createVmGroupData.autoscaleuserid = values.autoscaleuserid
@@ -2436,11 +2644,26 @@ export default {
         createVmGroupData = Object.fromEntries(
           Object.entries(createVmGroupData).filter(([key, value]) => value !== 
undefined))
 
+        const createVmGroupUserDataDetails = {}
+        var idx = 0
+        if (this.templateUserDataValues) {
+          for (const [key, value] of 
Object.entries(this.templateUserDataValues)) {
+            createVmGroupUserDataDetails['userdatadetails[' + idx + '].' + 
`${key}`] = value
+            idx++
+          }
+        }
+        if (isUserdataAllowed && this.userDataValues) {
+          for (const [key, value] of Object.entries(this.userDataValues)) {
+            createVmGroupUserDataDetails['userdatadetails[' + idx + '].' + 
`${key}`] = value
+            idx++
+          }
+        }
+
         this.processStatusModalVisible = true
         this.processStatus = null
 
         // create autoscale vm profile
-        const vmprofile = await this.createVmProfile(createVmGroupData)
+        const vmprofile = await this.createVmProfile(createVmGroupData, 
createVmGroupUserDataDetails)
 
         // create scaleup conditions and policy
         const scaleUpPolicyIds = []
@@ -2547,7 +2770,7 @@ export default {
       return new Promise((resolve) => {
         this.loading.zones = true
         const param = this.params.zones
-        const args = { listall: true, showicon: true }
+        const args = { showicon: true }
         if (zoneId) args.id = zoneId
         api(param.list, args).then(json => {
           const zoneResponse = (json.listzonesresponse.zone || []).filter(item 
=> item.securitygroupsenabled === false)
@@ -2579,7 +2802,7 @@ export default {
       param.loading = true
       param.opts = []
       const options = param.options || {}
-      if (!('listall' in options)) {
+      if (!('listall' in options) && !['zones', 'pods', 'clusters', 'hosts', 
'dynamicScalingVmConfig', 'hypervisors'].includes(name)) {
         options.listall = true
       }
       api(param.list, options).then((response) => {
@@ -2703,6 +2926,10 @@ export default {
       this.params[name].options = { ...this.params[name].options, ...options }
       this.fetchOptions(this.params[name], name)
     },
+    onUserdataTabChange (key, type) {
+      this[type] = key
+      this.userDataParams = []
+    },
     fetchTemplateNics (template) {
       var nics = []
       this.nicToNetworkSelection = []
diff --git a/ui/src/views/compute/ResetUserData.vue 
b/ui/src/views/compute/ResetUserData.vue
index 46561f15c1a..b9753f4bc19 100644
--- a/ui/src/views/compute/ResetUserData.vue
+++ b/ui/src/views/compute/ResetUserData.vue
@@ -335,7 +335,6 @@ export default {
       this.loadingData = true
       console.log(values)
       const params = {
-        id: this.resource.id
       }
       if (values.userdata && values.userdata.length > 0) {
         params.userdata = this.$toBase64AndURIEncoded(values.userdata)
@@ -356,7 +355,14 @@ export default {
           idx++
         }
       }
-      api('resetUserDataForVirtualMachine', params).then(json => {
+      params.id = this.resource.resetUserDataResourceId ? 
this.resource.resetUserDataResourceId : this.resource.id
+
+      const resetUserDataApiName = this.resource.resetUserDataApiName ? 
this.resource.resetUserDataApiName : 'resetUserDataForVirtualMachine'
+      const httpMethod = params.userdata ? 'POST' : 'GET'
+      const args = httpMethod === 'POST' ? {} : params
+      const data = httpMethod === 'POST' ? params : {}
+
+      api(resetUserDataApiName, args, httpMethod, data).then(json => {
         this.$message.success({
           content: `${this.$t('label.action.userdata.reset')} - 
${this.$t('label.success')}`,
           duration: 2


Reply via email to