From 7bd89d500b6b8b11b7fe7fd54d6178ddaf096641 Mon Sep 17 00:00:00 2001 From: Luis Toledo Date: Wed, 19 Nov 2025 16:51:38 -0300 Subject: [PATCH 1/5] add new feature to add the node auth in the generated inventory --- .../ansible/ansible/AnsibleDescribable.java | 8 ++ .../ansible/ansible/AnsibleRunner.java | 56 +++++++- .../ansible/AnsibleRunnerContextBuilder.java | 124 ++++++++++++++++-- .../AnsiblePlaybookInlineWorkflowStep.java | 2 + .../plugin/AnsiblePlaybookWorkflowStep.java | 4 +- .../plugins/ansible/util/AnsibleUtil.java | 10 ++ 6 files changed, 189 insertions(+), 15 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleDescribable.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleDescribable.java index 0919418f..5b8696b1 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleDescribable.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleDescribable.java @@ -100,6 +100,7 @@ public static String[] getValues() { public static final String ANSIBLE_INVENTORY_INLINE = "ansible-inventory-inline"; public static final String ANSIBLE_INVENTORY = "ansible-inventory"; public static final String ANSIBLE_GENERATE_INVENTORY = "ansible-generate-inventory"; + public static final String ANSIBLE_GENERATE_INVENTORY_NODES_AUTH = "ansible-generate-inventory-nodes-auth"; public static final String ANSIBLE_MODULE = "ansible-module"; public static final String ANSIBLE_MODULE_ARGS = "ansible-module-args"; public static final String ANSIBLE_DEBUG = "ansible-debug"; @@ -233,6 +234,13 @@ public static String[] getValues() { .description("Generate Ansible inventory from Rundeck nodes.") .build(); + static final Property GENERATE_INVENTORY_NODES_AUTH = PropertyBuilder.builder() + .booleanType(ANSIBLE_GENERATE_INVENTORY_NODES_AUTH) + .required(false) + .title("Generate inventory, pass node authentication from rundeck nodes") + .description("Pass authentication from rundeck nodes.") + .build(); + public static Property EXECUTABLE_PROP = PropertyUtil.freeSelect( ANSIBLE_EXECUTABLE, "Executable", diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java index ae653aea..2072f1a4 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java @@ -127,6 +127,15 @@ public static AnsibleRunner buildAnsibleRunner(AnsibleRunnerContextBuilder conte ansibleRunnerBuilder.inventory(inventory); } + Boolean generateInventoryNodeAuth = contextBuilder.generateInventoryNodesAuth(); + if(generateInventoryNodeAuth){ + Map> nodesAuth = contextBuilder.getNodesAuthenticationMap(); + if (nodesAuth != null && !nodesAuth.isEmpty()) { + ansibleRunnerBuilder.addNodeAuthToInventory(true); + ansibleRunnerBuilder.nodesAuthentication(nodesAuth); + } + } + String limit = contextBuilder.getLimit(); if (limit != null) { ansibleRunnerBuilder.limits(limit); @@ -291,9 +300,13 @@ public static AnsibleRunner buildAnsibleRunner(AnsibleRunnerContextBuilder conte File tempSshVarsFile ; File tempBecameVarsFile ; File vaultPromptFile; + File tempNodeAuthFile = null; String customTmpDirPath; + Boolean addNodeAuthToInventory; + Map> nodesAuthentication; + public void deleteTempDirectory(Path tempDirectory) throws IOException { Files.walkFileTree(tempDirectory, new SimpleFileVisitor() { @Override @@ -396,6 +409,40 @@ public int run() throws Exception { if (inventory != null && !inventory.isEmpty()) { procArgs.add("-i"); procArgs.add(inventory); + + if(addNodeAuthToInventory) { + Map hostUsers = new LinkedHashMap<>(); + Map hostPasswords = new LinkedHashMap<>(); + nodesAuthentication.forEach((nodeName, authValues) -> { + String user = authValues.get("ansible_user"); + String password = authValues.get("ansible_password"); + if (user != null) { + hostUsers.put(nodeName, user); + } + if (password != null) { + String encryptedPassword = password; + if (useAnsibleVault) { + try { + encryptedPassword = encryptExtraVarsKey(password); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + hostPasswords.put(nodeName, encryptedPassword); + } + }); + // Build YAML structure + Map yamlData = new LinkedHashMap<>(); + yamlData.put("host_passwords", hostPasswords); + yamlData.put("host_users", hostUsers); + try { + String yamlContent = mapperYaml.writeValueAsString(yamlData); + tempNodeAuthFile = AnsibleUtil.createTemporaryFile("", "all.yaml", yamlContent, customTmpDirPath); + } catch (IOException e) { + throw new RuntimeException("Failed to write all.yaml for node auth", e); + } + } + } if (limits != null && limits.size() == 1) { @@ -513,8 +560,6 @@ public int run() throws Exception { //SET env variables Map processEnvironment = new HashMap<>(); - - if (configFile != null && !configFile.isEmpty()) { if (debug) { System.out.println(" ANSIBLE_CONFIG: " + configFile); @@ -663,6 +708,10 @@ public int run() throws Exception { vaultPromptFile.deleteOnExit(); } + if (tempNodeAuthFile != null && !tempNodeAuthFile.delete()) { + tempNodeAuthFile.deleteOnExit(); + } + if (usingTempDirectory && !retainTempDirectory) { deleteTempDirectory(baseDirectory); } @@ -824,4 +873,5 @@ public String encryptExtraVarsKey(String extraVars) throws Exception { } -} \ No newline at end of file +} + diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java index ef74733b..449ec7dd 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java @@ -193,17 +193,7 @@ public String getSshPassword() throws ConfigurationException { if (null != storagePath) { //look up storage value - Path path = PathUtil.asPath(storagePath); - try { - ResourceMeta contents = context.getStorageTree().getResource(path) - .getContents(); - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - contents.writeContent(byteArrayOutputStream); - return byteArrayOutputStream.toString(); - } catch (StorageException | IOException e) { - throw new ConfigurationException("Failed to read the ssh password for " + - "storage path: " + storagePath + ": " + e.getMessage()); - } + return getPasswordFromPath(storagePath); } else { return null; @@ -211,6 +201,21 @@ public String getSshPassword() throws ConfigurationException { } } + public String getPasswordFromPath(String storagePath) throws ConfigurationException { + //look up storage value + Path path = PathUtil.asPath(storagePath); + try { + ResourceMeta contents = context.getStorageTree().getResource(path) + .getContents(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + contents.writeContent(byteArrayOutputStream); + return byteArrayOutputStream.toString(); + } catch (StorageException | IOException e) { + throw new ConfigurationException("Failed to read the ssh password for " + + "storage path: " + storagePath + ": " + e.getMessage()); + } + } + public Integer getSSHTimeout() throws ConfigurationException { Integer timeout = null; final String stimeout = PropertyResolver.resolveProperty( @@ -249,6 +254,23 @@ public String getSshUser() { return user; } + public String getSshNodeUser(INodeEntry node) { + final String user; + user = PropertyResolver.resolveProperty( + AnsibleDescribable.ANSIBLE_SSH_USER, + null, + getFrameworkProject(), + getFramework(), + node, + getJobConf() + ); + + if (null != user && user.contains("${")) { + return DataContextUtils.replaceDataReferencesInString(user, getContext().getDataContext()); + } + return user; + } + public AuthenticationType getSshAuthenticationType() { String authType = PropertyResolver.resolveProperty( @@ -880,4 +902,84 @@ public Map getListOptions(){ } return options; } + + public Map> getNodesAuthenticationMap(){ + + Map> authenticationNodesMap = new HashMap<>(); + + this.context.getNodes().forEach((node) -> { + String keyPath = PropertyResolver.resolveProperty( + AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH, + null, + getFrameworkProject(), + getFramework(), + node, + getJobConf() + ); + + Map auth = new HashMap<>(); + + if(null!=keyPath){ + try { + auth.put("ansible_password",getPasswordFromPath(keyPath) ); + } catch (ConfigurationException e) { + throw new RuntimeException(e); + } + + } + String userName = getSshNodeUser(node); + + if(null!=userName){ + auth.put("ansible_user",userName ); + } + + authenticationNodesMap.put(node.getNodename(), auth); + }); + + return authenticationNodesMap; + } + + + public List getListNodesKeyPath(){ + + List secretPaths = new ArrayList<>(); + + this.context.getNodes().forEach((node) -> { + String keyPath = PropertyResolver.resolveProperty( + AnsibleDescribable.ANSIBLE_SSH_KEYPATH, + null, + getFrameworkProject(), + getFramework(), + node, + getJobConf() + ); + + if(null!=keyPath){ + if(!secretPaths.contains(keyPath)){ + secretPaths.add(keyPath); + } + + } + }); + + return secretPaths; + } + + + public Boolean generateInventoryNodesAuth() { + Boolean generateInventoryNodesAuth = null; + String sgenerateInventoryNodesAuth = PropertyResolver.resolveProperty( + AnsibleDescribable.ANSIBLE_GENERATE_INVENTORY_NODES_AUTH, + null, + getFrameworkProject(), + getFramework(), + getNode(), + getJobConf() + ); + + if (null != sgenerateInventoryNodesAuth) { + generateInventoryNodesAuth = Boolean.parseBoolean(sgenerateInventoryNodesAuth); + } + return generateInventoryNodesAuth; + } } diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java index 8a0a09ad..acf2f979 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java @@ -43,6 +43,8 @@ public class AnsiblePlaybookInlineWorkflowStep implements StepPlugin, AnsibleDes builder.property(PLAYBOOK_INLINE_PROP); builder.property(EXTRA_VARS_PROP); builder.property(CONFIG_ENCRYPT_EXTRA_VARS); + builder.property(GENERATE_INVENTORY_PROP); + builder.property(GENERATE_INVENTORY_NODES_AUTH); builder.property(INVENTORY_INLINE_PROP); builder.property(VAULT_KEY_FILE_PROP); builder.property(VAULT_KEY_STORAGE_PROP); diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java index 1f5ba521..0fef6462 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java @@ -43,6 +43,8 @@ public class AnsiblePlaybookWorkflowStep implements StepPlugin, AnsibleDescribab builder.property(EXTRA_VARS_PROP); builder.property(CONFIG_ENCRYPT_EXTRA_VARS); builder.property(INVENTORY_INLINE_PROP); + builder.property(GENERATE_INVENTORY_PROP); + builder.property(GENERATE_INVENTORY_NODES_AUTH); builder.property(VAULT_KEY_FILE_PROP); builder.property(VAULT_KEY_STORAGE_PROP); builder.property(EXTRA_ATTRS_PROP); @@ -127,7 +129,7 @@ public Description getDescription() { @Override public List listSecretsPathWorkflowStep(ExecutionContext context, Map configuration) { AnsibleRunnerContextBuilder builder = new AnsibleRunnerContextBuilder(context, context.getFramework(), context.getNodes(), configuration); - return AnsibleUtil.getSecretsPath(builder); + return AnsibleUtil.getSecretsPathWorkflowSteps(builder); } @Override public Map getRuntimeProperties(ExecutionContext context) { diff --git a/src/main/groovy/com/rundeck/plugins/ansible/util/AnsibleUtil.java b/src/main/groovy/com/rundeck/plugins/ansible/util/AnsibleUtil.java index 126a36f7..9be0accd 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/util/AnsibleUtil.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/util/AnsibleUtil.java @@ -86,6 +86,16 @@ public static List getSecretsPath(AnsibleRunnerContextBuilder builder){ } + public static List getSecretsPathWorkflowSteps(AnsibleRunnerContextBuilder builder){ + List secretPaths = getSecretsPath(builder); + List secretPathsNodes =builder.getListNodesKeyPath(); + + if(secretPathsNodes!=null && !secretPathsNodes.isEmpty()){ + secretPaths.addAll(secretPathsNodes); + } + return secretPaths; + } + public static Map getRuntimeProperties(ExecutionContext context, String propertyPrefix) { Map properties = null; From de654c6953feceb348e8fc953af61d4c0bb1aad3 Mon Sep 17 00:00:00 2001 From: Luis Toledo Date: Thu, 11 Dec 2025 16:05:55 -0300 Subject: [PATCH 2/5] fix getting ssh-password from nodes --- .../plugins/ansible/ansible/AnsibleRunnerContextBuilder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java index 449ec7dd..3fa916bb 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java @@ -946,7 +946,7 @@ public List getListNodesKeyPath(){ this.context.getNodes().forEach((node) -> { String keyPath = PropertyResolver.resolveProperty( - AnsibleDescribable.ANSIBLE_SSH_KEYPATH, + AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH, null, getFrameworkProject(), getFramework(), @@ -958,7 +958,6 @@ public List getListNodesKeyPath(){ if(!secretPaths.contains(keyPath)){ secretPaths.add(keyPath); } - } }); From 1fa16df32c71429dbc5b841070f3b7e3213790a0 Mon Sep 17 00:00:00 2001 From: Eduardo Baltra Date: Thu, 11 Dec 2025 20:06:20 -0300 Subject: [PATCH 3/5] group_vars and properties in nodeexecutor --- .../ansible/ansible/AnsibleRunner.java | 41 ++++++++++++++++--- .../ansible/plugin/AnsibleNodeExecutor.java | 11 ++++- .../AnsiblePlaybookInlineWorkflowStep.java | 2 - .../plugin/AnsiblePlaybookWorkflowStep.java | 2 - 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java index 2072f1a4..8947afb6 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java @@ -128,7 +128,7 @@ public static AnsibleRunner buildAnsibleRunner(AnsibleRunnerContextBuilder conte } Boolean generateInventoryNodeAuth = contextBuilder.generateInventoryNodesAuth(); - if(generateInventoryNodeAuth){ + if(generateInventoryNodeAuth != null && generateInventoryNodeAuth){ Map> nodesAuth = contextBuilder.getNodesAuthenticationMap(); if (nodesAuth != null && !nodesAuth.isEmpty()) { ansibleRunnerBuilder.addNodeAuthToInventory(true); @@ -300,11 +300,13 @@ public static AnsibleRunner buildAnsibleRunner(AnsibleRunnerContextBuilder conte File tempSshVarsFile ; File tempBecameVarsFile ; File vaultPromptFile; - File tempNodeAuthFile = null; + File tempNodeAuthFile; + File groupVarsDir; String customTmpDirPath; - Boolean addNodeAuthToInventory; + @Builder.Default + Boolean addNodeAuthToInventory = false; Map> nodesAuthentication; public void deleteTempDirectory(Path tempDirectory) throws IOException { @@ -410,7 +412,7 @@ public int run() throws Exception { procArgs.add("-i"); procArgs.add(inventory); - if(addNodeAuthToInventory) { + if(addNodeAuthToInventory != null && addNodeAuthToInventory && nodesAuthentication != null && !nodesAuthentication.isEmpty()) { Map hostUsers = new LinkedHashMap<>(); Map hostPasswords = new LinkedHashMap<>(); nodesAuthentication.forEach((nodeName, authValues) -> { @@ -437,7 +439,30 @@ public int run() throws Exception { yamlData.put("host_users", hostUsers); try { String yamlContent = mapperYaml.writeValueAsString(yamlData); - tempNodeAuthFile = AnsibleUtil.createTemporaryFile("", "all.yaml", yamlContent, customTmpDirPath); + + // Create group_vars directory structure + File inventoryFile = new File(inventory); + File inventoryParentDir = inventoryFile.getParentFile(); + + if (inventoryParentDir != null) { + groupVarsDir = new File(inventoryParentDir, "group_vars"); + + if (!groupVarsDir.exists()) { + if (!groupVarsDir.mkdirs()) { + throw new RuntimeException("Failed to create group_vars directory at: " + groupVarsDir.getAbsolutePath()); + } + } + + // Create all.yaml in group_vars directory + tempNodeAuthFile = new File(groupVarsDir, "all.yaml"); + java.nio.file.Files.writeString(tempNodeAuthFile.toPath(), yamlContent); + tempNodeAuthFile.deleteOnExit(); + groupVarsDir.deleteOnExit(); + } else { + // Fallback to temp file if inventory has no parent directory + tempNodeAuthFile = AnsibleUtil.createTemporaryFile("group_vars", "all.yaml", yamlContent, customTmpDirPath); + } + } catch (IOException e) { throw new RuntimeException("Failed to write all.yaml for node auth", e); } @@ -712,6 +737,12 @@ public int run() throws Exception { tempNodeAuthFile.deleteOnExit(); } + if (groupVarsDir != null && groupVarsDir.exists()) { + if (!groupVarsDir.delete()) { + groupVarsDir.deleteOnExit(); + } + } + if (usingTempDirectory && !retainTempDirectory) { deleteTempDirectory(baseDirectory); } diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleNodeExecutor.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleNodeExecutor.java index f7421c3b..e75ae84e 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleNodeExecutor.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleNodeExecutor.java @@ -36,6 +36,7 @@ public class AnsibleNodeExecutor implements NodeExecutor, AnsibleDescribable, Pr builder.property(WINDOWS_EXECUTABLE_PROP); builder.property(CONFIG_FILE_PATH); builder.property(GENERATE_INVENTORY_PROP); + builder.property(GENERATE_INVENTORY_NODES_AUTH); builder.property(SSH_AUTH_TYPE_PROP); builder.property(SSH_USER_PROP); builder.property(SSH_PASSWORD_STORAGE_PROP); @@ -63,6 +64,8 @@ public class AnsibleNodeExecutor implements NodeExecutor, AnsibleDescribable, Pr builder.frameworkMapping(ANSIBLE_CONFIG_FILE_PATH,FWK_PROP_PREFIX + ANSIBLE_CONFIG_FILE_PATH); builder.mapping(ANSIBLE_GENERATE_INVENTORY,PROJ_PROP_PREFIX + ANSIBLE_GENERATE_INVENTORY); builder.frameworkMapping(ANSIBLE_GENERATE_INVENTORY,FWK_PROP_PREFIX + ANSIBLE_GENERATE_INVENTORY); + builder.mapping(ANSIBLE_GENERATE_INVENTORY_NODES_AUTH,PROJ_PROP_PREFIX + ANSIBLE_GENERATE_INVENTORY_NODES_AUTH); + builder.frameworkMapping(ANSIBLE_GENERATE_INVENTORY_NODES_AUTH,FWK_PROP_PREFIX + ANSIBLE_GENERATE_INVENTORY_NODES_AUTH); builder.mapping(ANSIBLE_SSH_AUTH_TYPE,PROJ_PROP_PREFIX + ANSIBLE_SSH_AUTH_TYPE); builder.frameworkMapping(ANSIBLE_SSH_AUTH_TYPE,FWK_PROP_PREFIX + ANSIBLE_SSH_AUTH_TYPE); builder.mapping(ANSIBLE_SSH_USER,PROJ_PROP_PREFIX + ANSIBLE_SSH_USER); @@ -197,7 +200,13 @@ public List listSecretsPath(ExecutionContext context, INodeEntry node) { jobConf.put(AnsibleDescribable.ANSIBLE_LIMIT,node.getNodename()); AnsibleRunnerContextBuilder builder = new AnsibleRunnerContextBuilder(node, context, context.getFramework(), jobConf); - return AnsibleUtil.getSecretsPath(builder); + List secretPaths = AnsibleUtil.getSecretsPath(builder); + List secretPathsNodes = builder.getListNodesKeyPath(); + + if(secretPathsNodes != null && !secretPathsNodes.isEmpty()){ + secretPaths.addAll(secretPathsNodes); + } + return secretPaths; } } diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java index acf2f979..8a0a09ad 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowStep.java @@ -43,8 +43,6 @@ public class AnsiblePlaybookInlineWorkflowStep implements StepPlugin, AnsibleDes builder.property(PLAYBOOK_INLINE_PROP); builder.property(EXTRA_VARS_PROP); builder.property(CONFIG_ENCRYPT_EXTRA_VARS); - builder.property(GENERATE_INVENTORY_PROP); - builder.property(GENERATE_INVENTORY_NODES_AUTH); builder.property(INVENTORY_INLINE_PROP); builder.property(VAULT_KEY_FILE_PROP); builder.property(VAULT_KEY_STORAGE_PROP); diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java index 0fef6462..762ad794 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookWorkflowStep.java @@ -43,8 +43,6 @@ public class AnsiblePlaybookWorkflowStep implements StepPlugin, AnsibleDescribab builder.property(EXTRA_VARS_PROP); builder.property(CONFIG_ENCRYPT_EXTRA_VARS); builder.property(INVENTORY_INLINE_PROP); - builder.property(GENERATE_INVENTORY_PROP); - builder.property(GENERATE_INVENTORY_NODES_AUTH); builder.property(VAULT_KEY_FILE_PROP); builder.property(VAULT_KEY_STORAGE_PROP); builder.property(EXTRA_ATTRS_PROP); From 71b4af994b7b0e80cdab5bb5471e51a3e187589d Mon Sep 17 00:00:00 2001 From: Eduardo Baltra Date: Thu, 18 Dec 2025 22:03:05 -0300 Subject: [PATCH 4/5] Added Inline Playbook option for testing --- .../plugin/AnsiblePlaybookInlineWorkflowNodeStep.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowNodeStep.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowNodeStep.java index a9a1d40c..5827ccc6 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowNodeStep.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsiblePlaybookInlineWorkflowNodeStep.java @@ -43,6 +43,8 @@ public class AnsiblePlaybookInlineWorkflowNodeStep implements NodeStepPlugin, An builder.property(PLAYBOOK_INLINE_PROP); builder.property(EXTRA_VARS_PROP); builder.property(CONFIG_ENCRYPT_EXTRA_VARS); + builder.property(GENERATE_INVENTORY_PROP); + builder.property(GENERATE_INVENTORY_NODES_AUTH); builder.property(VAULT_KEY_FILE_PROP); builder.property(VAULT_KEY_STORAGE_PROP); builder.property(EXTRA_ATTRS_PROP); @@ -121,7 +123,13 @@ public void executeNodeStep( @Override public List listSecretsPathWorkflowNodeStep(ExecutionContext context, INodeEntry node, Map configuration) { AnsibleRunnerContextBuilder builder = new AnsibleRunnerContextBuilder(node, context, context.getFramework(), configuration); - return AnsibleUtil.getSecretsPath(builder); + List secretPaths = AnsibleUtil.getSecretsPath(builder); + List secretPathsNodes = builder.getListNodesKeyPath(); + + if(secretPathsNodes != null && !secretPathsNodes.isEmpty()){ + secretPaths.addAll(secretPathsNodes); + } + return secretPaths; } @Override From 70d64d2f7ec9fa65499e6b3c6233a39702070c9d Mon Sep 17 00:00:00 2001 From: Eduardo Baltra Date: Tue, 23 Dec 2025 22:09:55 -0300 Subject: [PATCH 5/5] authentitacion debug logs --- .../ansible/AnsibleRunnerContextBuilder.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java index 3fa916bb..128b5046 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java @@ -209,7 +209,7 @@ public String getPasswordFromPath(String storagePath) throws ConfigurationExcept .getContents(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); contents.writeContent(byteArrayOutputStream); - return byteArrayOutputStream.toString(); + return byteArrayOutputStream.toString("UTF-8"); } catch (StorageException | IOException e) { throw new ConfigurationException("Failed to read the ssh password for " + "storage path: " + storagePath + ": " + e.getMessage()); @@ -907,6 +907,8 @@ public Map> getNodesAuthenticationMap(){ Map> authenticationNodesMap = new HashMap<>(); + System.err.println("DEBUG: getNodesAuthenticationMap called"); + this.context.getNodes().forEach((node) -> { String keyPath = PropertyResolver.resolveProperty( AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH, @@ -917,17 +919,28 @@ public Map> getNodesAuthenticationMap(){ getJobConf() ); + System.err.println("DEBUG: Node " + node.getNodename() + " keyPath: " + keyPath); + Map auth = new HashMap<>(); if(null!=keyPath){ try { - auth.put("ansible_password",getPasswordFromPath(keyPath) ); + String password = getPasswordFromPath(keyPath); + System.err.println("DEBUG: Retrieved password for " + node.getNodename() + ": " + (password != null ? password.substring(0, Math.min(3, password.length())) + "..." : "null")); + System.err.println("DEBUG: Password length: " + (password != null ? password.length() : 0)); + System.err.println("DEBUG: Password bytes: " + (password != null ? java.util.Arrays.toString(password.getBytes("UTF-8")) : "null")); + auth.put("ansible_password", password); } catch (ConfigurationException e) { + System.err.println("DEBUG: Error retrieving password for " + node.getNodename() + ": " + e.getMessage()); throw new RuntimeException(e); + } catch (Exception e2) { + System.err.println("DEBUG: Unexpected error: " + e2.getMessage()); + throw new RuntimeException(e2); } } String userName = getSshNodeUser(node); + System.err.println("DEBUG: Node " + node.getNodename() + " userName: " + userName); if(null!=userName){ auth.put("ansible_user",userName ); @@ -936,6 +949,7 @@ public Map> getNodesAuthenticationMap(){ authenticationNodesMap.put(node.getNodename(), auth); }); + System.err.println("DEBUG: authenticationNodesMap has " + authenticationNodesMap.size() + " entries"); return authenticationNodesMap; }