I am using Ansible and vCenter to provision a VM. When I run my playbook, I get an authentication error:
Cannot complete login due to an incorrect user name or password.
However, using the same credentials, I am able to log into vCenter manually.
Here is my simplified playbook:
---
- name: create a new VM on an ESX server
hosts: localhost
connection: local
tasks:
- name: include vars
include_vars:
dir: 'group_vars/prod'
files_matching: 'secret-esx.yml'
- name: gather facts from target host
local_action:
module: vmware_vm_facts
hostname: vi-devops-esx9.lab.vi.local
username: "{{ esx_username }}"
password: "{{ esx_password }}"
validate_certs: no
register: qe_facts
Why can I access vCenter, but vmware_vm_facts cannot with the same credentials?
My hostname was incorrect. Fixing my hostname fixed the authentication error.
Related
Use-Case:
We are deploying virtual machines into a cloud with a default linux image (Ubuntu 22.04 at the moment). After deploying a machine, we configure our default users and change the SSH port from 22 to 2222 with Ansible.
Side note: We are using a jump concept through the internet - Ansible automation platform / AWS => internet => SSH jump host => target host
To keep the possibility for Ansible to connect to the new machine, after changing the SSH port, I found multiple Stack Overflow / blog entries, checking and setting ansible_ssh_port, basically by running wait_for on port 22 and 2222 and set the SSH variable depending on the result (code below).
Right now this works fine for the first SSH host (jumphost), but always fails for the second host due to issues with establishing the ssh connection.
Side note: The SSH daemon is running. If I use my user from the jump host, I can get a SSH response from 22/2222 (depending on the current state of deployment).
Edit from questions:
The deployment tasks should only be run on the target host. Not the jumphost as well.
I run the deployment on the jumphost first and make sure it is up, running and configured.
After that, i run the deployment on all machines behind the jumphost to configure them.
This also ensures that if i ever would need reboot, that i don't kill all tunneled ssh session by accident.
Ansible inventory example
all:
hosts:
children:
jumphosts:
hosts:
example_jumphost:
ansible_host: 123.123.123.123
cloud_hosts:
hosts:
example_cloud_host01: #local DNS is resolved on the jumphost - no ansible_host here (yet)
ansible_ssh_common_args: '-oProxyCommand="ssh -W %h:%p -oStrictHostKeyChecking=no -q ansible#123.123.123.123 -p 2222"' #Tunnel through the appropriate jumphost
delegation_host: "ansible#123.123.123.123" #delegate jobs to the jumphost in each project if needed
vars:
ansible_ssh_port: 2222
SSH check_port role
- name: Set SSH port to 2222
set_fact:
ansible_ssh_port: 2222
- name: "Check backend port 2222"
wait_for:
port: 2222
state: "started"
host: "{{ inventory_hostname }}"
connect_timeout: "5"
timeout: "5"
# delegate_to: "{{ delegation_host }}"
# vars:
# ansible_ssh_port: 2222
ignore_errors: true
register: ssh_port
- name: "Check backend port 22"
wait_for:
port: "22"
state: "started"
host: "{{ inventory_hostname }}"
connect_timeout: "5"
timeout: "5"
# delegate_to: "{{ delegation_host }}"
# vars:
# ansible_ssh_port: 2222
ignore_errors: true
register: ssh_port_default
when:
- ssh_port is defined
- ssh_port.state is undefined
- name: Set backend SSH port to 22
set_fact:
ansible_ssh_port: 22
when:
- ssh_port_default.state is defined
The playbook itself
- hosts: "example_cloud_host01"
gather_facts: false
roles:
- role: check_port #check if we already have the correct port or need 22
- role: sshd #Set Port to 2222 and restart sshd
- role: check_port #check the port again, after it has been changed
- role: install_apps
- role: configure_apps
Error message:
with delegate_to for the task Check backend port 2222:
fatal: [example_cloud_host01 -> ansible#123.123.123.123]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 123.123.123.123 port 22: Connection refused", "unreachable": true}
This confuses me, because I expect the delegation host to use the same ansible_ssh_port as the target host.
Without delegate_to for task Check backend port 2222 and Check backend port 22:
fatal: [example_cloud_host01]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": false, "elapsed": 5, "msg": "Timeout when waiting for example_cloud_host01:2222"}
fatal: [example_cloud_host01]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": false, "elapsed": 5, "msg": "Timeout when waiting for example_cloud_host01:22"}
I have no idea why this happens. If I try the connection manually, it works fine.
What I tried so far:
I played around with delegate_to, vars, ... as mentioned above.
I wanted to see if I can provide delegato_to with the proper port 2222 for the jump host.
I wanted to see if can run this without delegate_to (since it should automatically use the proxy command to run on the jump host anyway).
Neither way gave me a solution on how to connect to my second tier servers after changing the SSH port.
Right now, I split the playbook into two
deploy sshd config with port 22
run our full deploy afterwards on port 2222
I would do the following (I somewhat tested this with fake values in the inventory using localhost as a jumphost to check ports on localhost as well)
Edit: modified my examples to somewhat try to show you a way after your comments on your question an on this answer
Inventory
---
all:
vars:
ansible_ssh_port: 2222
proxies:
vars:
ansible_user: ansible
hosts:
example_jumphost1:
ansible_host: 123.123.123.123
example_jumphost2:
ansible_host: 231.231.231.231
# ... and more jump hosts ...
cloud_hosts:
vars:
jump_vars: "{{ hostvars[jump_host] }}"
ansible_ssh_common_args: '-oProxyCommand="ssh -W %h:%p -oStrictHostKeyChecking=no -q {{ jump_vars.ansible_user }}#{{ jump_vars.ansible_host }} -p {{ jump_vars.ansible_shh_port | d(22) }}"'
children:
cloud_hosts_north:
vars:
jump_host: example_jumphost1
hosts:
example_cloud_host01:
example_cloud_host02:
# ... and more ...
cloud_hosts_south:
var:
jump_host: example_jumphost2
hosts:
example_cloud_host03:
example_cloud_host04:
# ... and more ...
# ... and more cloud groups ...
Tasks to check ports.
- name: "Check backend inventory configured port {{ ansible_ssh_port }}"
wait_for:
port: "{{ ansible_ssh_port }}"
state: "started"
host: "{{ inventory_hostname }}"
connect_timeout: "5"
timeout: "5"
delegate_to: "{{ jump_host }}"
ignore_errors: true
register: ssh_port
- name: "Check backend default ssh port if relevant"
wait_for:
port: "22"
state: "started"
host: "{{ inventory_hostname }}"
connect_timeout: "5"
timeout: "5"
delegate_to: "{{ jump_host }}"
ignore_errors: true
register: ssh_port_default
when: ssh_port is failed
- name: "Set backend SSH port to 22 if we did not change it yet"
set_fact:
ansible_ssh_port: 22
when:
- ssh_port_default is not skipped
- ssh_port_default is success
Please note that if checks for ports 22/2222 both fail, your configured port will still be 2222 but any later task will obviously fail. You might want to fail fast after checks for those relevant hosts:
- name: "Fail host if no port is available"
fail:
msg:
- "Host {{ inventory_hostname }}" does not have"
- "any ssh port available (tested 22 and 2222)"
when:
- ssh_port is failed
- ssh_port_default is failed
With this in place, you can use different targets on your play to reach the relevant hosts:
For jump hosts
Run on a single bastion host: e.g. hosts: example_jumphost1
Run on all bastion hosts: hosts: proxies
For cloud hosts
Run on all cloud hosts: hosts: cloud_hosts
Run on a single child group: e.g. hosts: cloud_hosts_north
Run on all cloud hosts except a subgroup: e.g. hosts: cloud_hosts:!cloud_hosts_south
For more see ansible patterns
Good morning all,
I'm racking my brains over a simple subject.
I'm on a "master" server and I would like to check if he manages to connect in SSH on a server list.
Example
ansible-playbook -i inventaire_test test_ssh.yml
---
tasks:
- name: test unreachable
ansible.builtin.ping:
register: test_ssh
ignore_unreachable: true
- name: test
fail:
msg: "test"
when: test_ssh.unreachable is defined
- name: header CSV
lineinfile:
insertafter: EOF
dest: /home/list.csv
line: "Server;OS;access"
delegate_to:localhost
- name: Info
lineinfile:
dest: /home/list.csv
line: "{{ inventory_hostname }};OK"
state: present
when: test_ssh is successful
delegate_to:localhost
- name: Info csv
lineinfile:
dest: /home/list.csv
line: "{{ inventory_hostname }};KO"
state: present
when: test_ssh.unreachable is undefined
delegate_to:localhost
I can't find a check_ssh module. There is ansible.builtin.ssh but I can't use it.
Do you have an idea?
Thanks in advance.
Regarding
I'm on a "master" server and I would like to check if he manages to connect in SSH on a server list. ... I can't find a check_ssh module.
According the documentation there is a
ping module – Try to connect to host, verify a usable python and return pong on success
... test module, this module always returns pong on successful contact. It does not make sense in playbooks, but it is useful from /usr/bin/ansible to verify the ability to login and that a usable Python is configured.
which seems to be doing what you are looking for.
I think I may be using vars_prompt incorrectly because when I define a variable (used as a host) from command line, the host is used for the following task correctly:
ansible-playbook newfile -v -e 'target_host=uat:prd'
- hosts '{{ target_host }}'
tasks:
...
But when I define the same variable using vars_prompt:
- name: run task
hosts: localhost
gather_facts: no
vars_prompt:
- name: target_host
prompt: please choose a host site
private: no
- hosts: '{{ target_host }}'
tasks:
...
I get error: 'target_host' is undefined pointing at the - hosts: '{{ target_host }}'
Note: it does ask the prompt before getting the error
Thank you for the suggestion to add to host group #JBone. Sadly I have already tried this approach and I get:
Failed to connect to the host via ssh: ssh: Could not resolve hostname uat:prd: Name or service not known
Even though if I fill the host in the playbook as uat:prd it runs on each host
this approach does work for uat or prd by themselves but not uat:prd
you should add this variable value to a new host group using add_host module.
- name: run task
hosts: localhost
gather_facts: no
vars_prompt:
- name: target_host
prompt: please choose a host site
private: no
tasks:
- name: add host
add_host:
name: "{{ target_host }}"
groups: new_hosts_grp
- hosts: new_hosts_grp
tasks:
...
try that one.
I'm following the documentation for creating an instance using Ansible
http://docs.ansible.com/ansible/guide_gce.html
However, when I run this I get:
Required 'compute.zones.list' permission for 'projects/quick-line-137923'
I don't know where I'm meant to configure these permissions for a service account, because the documentation seems to suggest that you can only configure permissions for a service account on an instance that is already created:
"You can set scopes only when you create a new instance"
When I try to grant IAM permissions for this service account (admin), it isn’t in the list and when I select the service account in ‘service accounts’ I’m asked to add a member for domain wide permissions, nowhere to assign permissions to this service account for the compute.zones.list
Any help?
My playbook looks like so:
- name: "Create instance(s)"
hosts: localhost
gather_facts: no
connection: local
vars:
machine_type: n1-standard-1 # default
image: ubuntu-1404-lts
service_account_email: admin-531#quick-line-137923.iam.gserviceaccount.com
credentials_file: /Users/Mike/Downloads/project.json
project_id: quick-line-137923
tasks:
- name: "Launch instances"
gce:
instance_names: dev
machine_type: "{{ machine_type }}"
image: "{{ image }}"
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
tags: webserver
register: gce
- name: "Wait for SSH to come up"
wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=60
with_items: gce.instance_data
- name: "Add host to groupname"
add_host: hostname={{ item.public_ip }} groupname=new_instances
with_items: gce.instance_data
- name: "Manage new instances"
vars_files:
- "vars/webserver.yml"
hosts: new_instances
connection: ssh
sudo: True
roles:
- geerlingguy.apache
- geerlingguy.php
- geerlingguy.drush
- geerlingguy.mysql
Add the Compute Instance Admin and Service Account Actor roles to the service account.
You also have to activate the service account. The gcloud tool can be used: https://cloud.google.com/sdk/gcloud/reference/auth/activate-service-account .
TL;DR: Is it possible to chain two playbooks with one ansible-playbook command where one playbook is password auth and the other playbook is key auth? (see last section for real-world purpose).
Setup:
I have two playbooks, the second of which includes the first.
PlaybookA.yml
---
- name: PlaybookA # requires password authentication
hosts: sub.domain.ext
remote_user: root
roles:
- { role: role1, sudo: yes }
...
PlaybookB.yml
---
- name: Run PlaybookA
include: PlaybookA.yml
- name: PlaybookB # requires ssh-key authentication
hosts: sub.domain.ext
remote_user: ansible
roles:
- { role: role2, sudo: yes }
...
Requirements:
Execute only one command.
Use password auth for PlaybookA.
Use ssh-key auth for PlaybookB.
Question 1:
Is it possible within Ansible (versions 1.9.4 or lower) to execute one ansible-playbook command that will successfully run PlaybookB using ssh-key authentication but when PlaybookB includes PlaybookA, run PlaybookA using password authentication?
Question 2:
If this is not possible with Ansible 1.9.4 or lower, is this possible with 2.0.0+?
Notes of worth:
Ansible provides --ask-pass (or -k) as a command line switch enabling password authentication.
Ansible provides ask_pass as a variable but it seems as though it can only be set within ansible.cfg (I haven't been able to set this as a playbook variable to the desired effect).
Attempting to set ask_pass as an instruction within a playbook results in the following: ERROR: ask_pass is not a legal parameter of an Ansible Play. If this parameter was legal, it would provide a way to instruct ansible on a per-playbook level, what authentication method to use.
Purpose / Real World:
I'm attempting to create a configuration management workflow with Ansible that will be simple enough that others at work will be able to learn / adapt to it (and hopefully the use of Ansible in general for CM and orchestration).
For any new machine (VM or physical) that gets built, I intend for us to run two playbooks immediately. PlaybookA (as shown above) has the responsibility of logging in with the correct default user (typically depends upon the infrastructure [aws, vsphere, none, etc]). Once in, its very limited job is to:
Create the standardized user for ansible to run as (and install its ssh-key).
Remove any non-root users that may exist (artifacts of the vm infrastructure, etc).
Disable root access.
Disable password authentication (ssh-key only from this point on).
Depending upon the vm infrastructure (or lack thereof), the default user or the default authentication method can be different. Toward the goal of adoption of Ansible, I'm attempting to keep things extremely simple for fellow co-workers, so I'd like to automate as much of this flow-control as possible.
Once PlaybookA has locked down the vm and setup the standardized user, PlaybookB uses that standardized user to perform all other operations necessary to bring our vm's up to the necessary baseline of tools and utilities, etc.
Any tips, hints, suggestions would be greatly appreciated.
I have been facing the same problem today. Two ideas may help you here:
You can ask for the password using vars_prompt in your playbook instead of --ask-pass
Set the password using set_fact:
- name: "set password for the play"
set_fact: ansible_ssh_pass="{{ my_pass }}"
You could store the password in a file, or prompt for it, as in the example below. In my example, the sshd config thats being created will forbid password logins, but using ansible defaults, you will be surprised that the second playbook will still be executed (!), even though I "forgot" to create an authorized_key. Thats due to the fact, that ansible uses the ControlPersist options of ssh, and simply keeps the connection between single tasks open. You can turn that off in ansible.cfg
Example Playbook:
- name: "MAKE BARE: Run preparatory steps on a newly acquired server"
hosts: blankee
tasks:
- name: "set password for the play"
set_fact: ansible_ssh_pass="{{ my_pass }}"
- name: "Create directory {{ pathsts }}/registry/ansible-init"
file: name="{{ pathsts }}/registry/ansible-init" state=directory owner=root group=www-data mode=770
- name: "copy sshd config file"
copy:
src: 'roles/newhost/files/sshd_config'
dest: '/etc/ssh/sshd_config'
owner: 'root'
group: 'root'
mode: '0644'
- name: "Check syntax of sshd configuration"
shell: sshd -t
register: result
changed_when: false
failed_when: "result.rc != 0"
- name: "Restart SSHD and enable Service to start at boot"
service: name=sshd state=restarted
changed_when: false
vars:
my_pass2: foobar
vars_prompt:
- name: "my_pass"
prompt: "########## Enter PWD:\n "
- name: "Second run: This should authenticate w/out password:"
hosts: blankee
tasks:
- name: "Create directory {{ pathsts }}/registry/ansible-init"
file: name="{{ pathsts }}/registry/ansible-init22" state=directory owner=root group=www-data mode=770
I don't know a way to change the authentication method within the play. I think I'd prefer running two different playbooks as Jenkins job or similar, but I can think of a pure Ansible workaround: instead of including the second playbook, you could get ansible to run a shell command as a local action, and run the command to execute the second playbook from the first one. Here's a rough proof of concept:
---
- hosts: all
vars_files:
- vars.yml
tasks:
- debug: msg="Run your first role here."
- name: Then call Ansible to run the second playbook.
local_action: shell ansible-playbook -i ~/workspace/hosts ~/workspace/second_playbook.yml
register: playbook_results
- debug: var=playbook_results.stdout_lines
Here's the output:
GATHERING FACTS ***************************************************************
ok: [vagrantbox]
TASK: [debug msg="Run your first role here."] *********************************
ok: [vagrantbox] => {
"msg": "Run your first role here."
}
TASK: [Then call Ansible to run the second playbook.] *************************
changed: [vagrantbox -> 127.0.0.1]
TASK: [debug var=playbook_results.stdout_lines] *******************************
ok: [vagrantbox] => {
"var": {
"playbook_results.stdout_lines": [
"",
"PLAY [Proof of concept] ******************************************************* ",
"",
"GATHERING FACTS *************************************************************** ",
"ok: [vagrantbox]",
"",
"TASK: [debug msg=\"This playbook was called from another playbook!\"] *********** ",
"ok: [vagrantbox] => {",
" \"msg\": \"This playbook was called from another playbook!\"",
"}",
"",
"PLAY RECAP ******************************************************************** ",
"vagrantbox : ok=2 changed=0 unreachable=0 failed=0 "
]
}
}
PLAY RECAP ********************************************************************
vagrantbox : ok=4 changed=1 unreachable=0 failed=0