Browse Source

Merge pull request #434 from abhishek-s-a/devel

Issue #433: Configure LDAP and 2FA PowerEdge Servers & Configure new devices
John Lockman 3 years ago
parent
commit
0f92901ef9
34 changed files with 1178 additions and 221 deletions
  1. 111 0
      control_plane/input_params/idrac_tools_vars.yml
  2. 51 0
      control_plane/roles/control_plane_common/tasks/encrypt_idrac_tools_vars.yml
  3. 32 30
      control_plane/roles/control_plane_common/tasks/fetch_base_inputs.yml
  4. 3 0
      control_plane/roles/control_plane_common/tasks/main.yml
  5. 6 3
      control_plane/roles/control_plane_common/tasks/nfs_server_setup.yml
  6. 16 10
      control_plane/roles/control_plane_common/tasks/password_config.yml
  7. 5 0
      control_plane/roles/control_plane_common/tasks/verify_omnia_params.yml
  8. 11 5
      control_plane/roles/control_plane_common/vars/main.yml
  9. 2 2
      control_plane/roles/control_plane_customiso/tasks/create_unattended_iso.yml
  10. 0 1
      control_plane/roles/control_plane_repo/files/R430_inv.xml
  11. 0 1
      control_plane/roles/control_plane_repo/files/poweredge_models.txt
  12. 137 142
      control_plane/roles/provision_idrac/tasks/check_prerequisites.yml
  13. 7 2
      control_plane/roles/provision_idrac/tasks/main.yml
  14. 41 20
      control_plane/roles/provision_idrac/tasks/update_firmware.yml
  15. 1 0
      control_plane/roles/provision_idrac/tasks/validate_idrac_vars.yml
  16. 3 0
      control_plane/roles/provision_idrac/vars/main.yml
  17. 21 0
      control_plane/tools/configure_new_devices.yml
  18. 21 0
      control_plane/tools/idrac_2fa.yml
  19. 21 0
      control_plane/tools/idrac_ldap.yml
  20. 104 0
      control_plane/tools/roles/configure_new_devices/tasks/main.yml
  21. 20 0
      control_plane/tools/roles/configure_new_devices/vars/main.yml
  22. 54 0
      control_plane/tools/roles/idrac_2fa/tasks/configure_2fa.yml
  23. 77 0
      control_plane/tools/roles/idrac_2fa/tasks/configure_smtp.yml
  24. 28 0
      control_plane/tools/roles/idrac_2fa/tasks/main.yml
  25. 96 0
      control_plane/tools/roles/idrac_2fa/tasks/validate_2fa_vars.yml
  26. 34 0
      control_plane/tools/roles/idrac_2fa/vars/main.yml
  27. 60 0
      control_plane/tools/roles/idrac_ldap/tasks/configure_ldap.yml
  28. 23 0
      control_plane/tools/roles/idrac_ldap/tasks/main.yml
  29. 141 0
      control_plane/tools/roles/idrac_ldap/tasks/validate_ldap_vars.yml
  30. 33 0
      control_plane/tools/roles/idrac_ldap/vars/main.yml
  31. 4 1
      control_plane/tools/roles/idrac_secure_boot/tasks/configure_secure_boot.yml
  32. 6 2
      control_plane/tools/roles/idrac_system_lockdown/tasks/check_prerequisites.yml
  33. 5 2
      control_plane/tools/roles/idrac_system_lockdown/tasks/configure_system_lockdown.yml
  34. 4 0
      platforms/roles/kubeflow/tasks/main.yml

+ 111 - 0
control_plane/input_params/idrac_tools_vars.yml

@@ -0,0 +1,111 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+### Usage: idrac_2fa ###
+
+# Specify 2FA related paramters when two_factor_authentication is enabled in idrac_vars.yml
+# By default two factor authentication will be "disabled"
+# If required it can be "enabled" in idrac_vars.yml
+# [WARNING] Once 2FA is enabled, user has to disable 2FA manually. Other iDRAC playbooks won't run if 2FA is enabled
+# The SMTP server details should be valid. 
+# 2FA will be enabled only if email notification is working using SMTP.
+
+# DNS domain name to set to iDRAC
+# Mandatory value required
+dns_domain_name: ""
+
+# IPV4 static DNS1 and DNS2
+# Mandatory value required
+ipv4_static_dns1: ""
+ipv4_static_dns2: ""
+
+# Server IP used for SMTP
+# Mandatory value required
+smtp_server_ip: ""
+
+# Username used for SMTP
+# Mandatory value required
+smtp_username: ""
+   
+# Password used for SMTP
+# Mandatory value required    
+smtp_password: ""
+
+# Email address used for enabling 2FA
+# Mandatory value required
+use_email_address_2fa: ""
+
+
+### Usage: idrac_ldap ###
+
+# Specify LDAP related paramters when ldap_directory_services is enabled in idrac_vars.yml
+# By default LDAP directory services will be "disabled"
+# If required it can be "enabled" in idrac_vars.yml
+# The LDAP server details should be valid.
+
+# CA certification validation value
+# cert_validation_enable supports only disabled
+# CA Certificate can't be upload using this playbook idrac_ldap.yml.
+# If required user has to manually upload CA certificate after idrac_ldap.yml execution.
+cert_validation_enable: "disabled"
+
+# Sever address used for LDAP
+# Mandatory value required
+# Recommended to provided LDAP server ip address instead of FQDN
+ldap_server_address: ""
+
+# TCP port port at which the LDAP server is listening for connections
+# Default port for LDAP: 389
+# Default port for LDAP over SSL: 636
+ldap_port: "636"
+
+# Distinguished Name of the node in your directory tree from which to start searching for records
+# For example: cn=Administrator,cn=Users,dc=mycompany,dc=com
+bind_dn: ""
+
+# Password used for bind_dn
+bind_password: ""
+
+# The distinguished name of the search base. 
+# For example: dc=mycompany,dc=com
+# Mandatory value required
+base_dn: ""
+
+# User attribute used for search in LDAP server
+user_attribute: ""
+
+# Group attribute used for search in LDAP server
+group_attribute: ""
+
+# Specify group attribute type is DN or not
+# Supported options are "enabled" or "disabled"
+# By default group_attribute_is_dn will be disabled
+group_attribute_is_dn: "disabled"
+
+# Search scope is related to the Base DN. 
+# The search scope defines how LDAP will search for your objects.
+search_filter: ""
+
+# DN of LDAP group be to added
+# Supports adding only one role group
+# For example: cn=Admins,cn=Group,dc=mycompany,dc=com
+# Mandatory value required
+role_group1_dn: ""
+
+# Privielege to LDAP role group 1
+# Supported options are Administrator, Operator, ReadOnly
+# By default role_group1_privilege will be Administrator
+# Mandatory value required
+role_group1_privilege: "Administrator"

+ 51 - 0
control_plane/roles/control_plane_common/tasks/encrypt_idrac_tools_vars.yml

@@ -0,0 +1,51 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Check if idrac_vault_key exists
+  stat:
+    path: "{{ idrac_tools_vaultname }}"
+  register: idrac_vault_key_result
+
+- name: Create ansible vault key if it does not exist
+  set_fact:
+    idrac_vault_key: "{{ lookup('password', '/dev/null chars=ascii_letters') }}"
+  when: not idrac_vault_key_result.stat.exists
+
+- name: Save vault key
+  copy:
+    dest: "{{ idrac_tools_vaultname }}"
+    content: |
+      {{ idrac_vault_key }}
+    owner: root
+    force: yes
+    mode: "{{ vault_file_perm }}"
+  when: not idrac_vault_key_result.stat.exists
+
+- name: Check if idrac_tools_vars.yml file is encrypted
+  command: cat {{ idrac_tools_vars_filename }}
+  changed_when: false
+  register: idrac_tools_content
+  no_log: True
+
+- name: Encrpyt idrac_tools_vars.yml
+  command: >-
+    ansible-vault encrypt {{ idrac_tools_vars_filename }} --vault-password-file {{ idrac_tools_vaultname }}
+  changed_when: true
+  when: "'$ANSIBLE_VAULT;' not in idrac_tools_content.stdout"
+
+- name: Update idrac_tools_vars.yml permission
+  file:
+    path: "{{ idrac_tools_vars_filename }}"
+    mode: "{{ vault_file_perm }}"

+ 32 - 30
control_plane/roles/control_plane_common/tasks/fetch_base_inputs.yml

@@ -36,15 +36,17 @@
       host_network_dhcp_start_range | length < 1 or
       host_network_dhcp_end_range | length < 1
 
-#- name: Validate infiniband base_vars are not empty
-#  fail:
-#    msg: "{{ input_base_failure_msg }} for infiniBand as ib_switch_support is true"
-#  register: ib_check
-#  when:
-#    - ib_network_nic | length < 1 or
-#      ib_network_dhcp_start_range | length < 1 or
-#      ib_network_dhcp_end_range | length < 1
-#  when: ib_switch_support
+- name: Validate infiniband base_vars are not empty
+  assert:
+    that:
+      - ib_network_nic | length > 2
+      - ib_network_dhcp_start_range | length > 6
+      - ib_network_dhcp_end_range | length > 6
+    success_msg: "{{ success_msg_ib }}"
+    fail_msg: "{{ fail_msg_ib }}"
+  register: ib_check
+  when: ib_switch_support
+
 
 - name: Set facts to validate snmp support
   set_fact:
@@ -124,7 +126,7 @@
   shell: set -o pipefail && ip a | awk '/state UP/{print $2}'
   register: nic_addr_up
   changed_when: false
-  
+
 - name: Assert public nic
   assert:
     that:
@@ -191,12 +193,12 @@
   when: ( result_path_iso_file.stat.exists ) and ( ".iso" not in iso_file_path )
 
 ####management_net_dhcp_start_end_range
-#- name: Assert management network nic
-#  assert:
-#    that:
-#      - mngmnt_network_nic in nic_addr_up.stdout
-#    success_msg: "{{ success_msg_mngmnt_network_nic }}"
-#    fail_msg: "{{ fail_msg_mngmnt_network_nic }}"
+- name: Assert management network nic
+  assert:
+    that:
+      - mngmnt_network_nic in nic_addr_up.stdout
+    success_msg: "{{ success_msg_mngmnt_network_nic }}"
+    fail_msg: "{{ fail_msg_mngmnt_network_nic }}"
 
 - name: Fetch the management network ip, netmask and subnet
   set_fact:
@@ -270,12 +272,12 @@
 #########
 
 ###Host network####
-#- name: Assert host network nic
-#  assert:
-#    that:
-#      - host_network_nic in nic_addr_up.stdout
-#    success_msg: "{{ success_msg_host_network_nic }}"
-#    fail_msg: "{{ fail_msg_host_network_nic }}"
+- name: Assert host network nic
+  assert:
+    that:
+      - host_network_nic in nic_addr_up.stdout
+    success_msg: "{{ success_msg_host_network_nic }}"
+    fail_msg: "{{ fail_msg_host_network_nic }}"
 
 - name: Fetch the host network ip, netmask and subnet
   set_fact:
@@ -357,13 +359,13 @@
     fail_msg: "{{ fail_msg_different_nics }}"
 
 ########
-#- name: Assert infiniband network nic
-#  assert:
-#    that:
-#      - ib_network_nic in nic_addr_up.stdout
-#    success_msg: "{{ success_msg_ib_network_nic }}"
-#    fail_msg: "{{ fail_msg_ib_network_nic }}"
-#  when: ib_switch_support
+- name: Assert infiniband network nic
+  assert:
+    that:
+      - ib_network_nic in nic_addr_up.stdout
+    success_msg: "{{ success_msg_ib_network_nic }}"
+    fail_msg: "{{ fail_msg_ib_network_nic }}"
+  when: ib_switch_support
 
 - name: Fetch the infiniband network ip, netmask and subnet
   set_fact:
@@ -450,4 +452,4 @@
       - ib_network_nic != host_network_nic
     success_msg: "{{ success_msg_different_nics_ib }}"
     fail_msg: "{{ fail_msg_different_nics_ib }}"
-  when: ib_switch_support
+  when: ib_switch_support

+ 3 - 0
control_plane/roles/control_plane_common/tasks/main.yml

@@ -38,5 +38,8 @@
   import_tasks: fetch_sm_inputs.yml
   when: ib_switch_support
 
+- name: Encrypt idrac_tools_vars.yml
+  import_tasks: encrypt_idrac_tools_vars.yml
+
 - name: NFS Server setup for offline repo and awx
   import_tasks: nfs_server_setup.yml

+ 6 - 3
control_plane/roles/control_plane_common/tasks/nfs_server_setup.yml

@@ -50,10 +50,13 @@
 - name: Adding NFS share entries in /etc/exports
   lineinfile:
     path: "{{ exports_file_path }}"
-    line: "{{ item }} {{ ansible_default_ipv4.address }}(rw,sync,no_root_squash)"
+    line: "{{ item.path }} {{ item.ip }}(rw,sync,no_root_squash)"
   with_items:
-    - "{{ nfs_share_offline_repo }}"
-    - "{{ nfs_share_awx }}"
+    - { path: "{{ nfs_share_offline_repo }}", ip: "{{ mngmnt_network_ip }}" }
+    - { path: "{{ nfs_share_offline_repo }}", ip: "{{ public_ip }}" }
+    - { path: "{{ nfs_share_awx }}", ip: "{{ mngmnt_network_ip }}" }
+    - { path: "{{ nfs_share_awx }}", ip: "{{ public_ip }}" }
+    - { path: "{{ nfs_share_offline_repo }}", ip: "{{ mngmnt_network_subnet }}/{{ mngmnt_network_netmask }}" }
 
 - name: Exporting the shared directories
   command: exportfs -r

+ 16 - 10
control_plane/roles/control_plane_common/tasks/password_config.yml

@@ -17,6 +17,7 @@
   command: cat {{ login_vars_filename }}
   changed_when: false
   register: config_content
+  no_log: true
 
 - name: Decrpyt login_vars.yml
   command: >-
@@ -37,7 +38,7 @@
     - provision_password | length < 1 or
       cobbler_password | length < 1 or      
       idrac_username | length < 1 or
-      idrac_password | length < 1      
+      idrac_password | length < 1
 
 - name: Assert provision_password
   assert:
@@ -50,7 +51,6 @@
       - " \"'\" not in provision_password "
     success_msg: "{{ success_msg_provision_password }}"
     fail_msg: "{{ fail_msg_provision_password }}"
-  no_log: true
   register: provision_password_check
 
 - name: Assert cobbler_password
@@ -64,7 +64,6 @@
       - " \"'\" not in cobbler_password "
     success_msg: "{{ success_msg_cobbler_password }}"
     fail_msg: "{{ fail_msg_cobbler_password }}"
-  no_log: true
   register: cobbler_password_check
 
 - name: Assert idrac_username
@@ -78,7 +77,6 @@
       - " \"'\" not in idrac_username "
     success_msg: "{{ success_idrac_username }}"
     fail_msg: "{{ fail_idrac_username }}"
-  no_log: true
 
 - name: Assert idrac_password
   assert:
@@ -91,7 +89,6 @@
       - " \"'\" not in idrac_password "
     success_msg: "{{ success_msg_idrac_password }}"
     fail_msg: "{{ fail_msg_idrac_password }}"
-  no_log: true
   register: idrac_password_check
 
 - name: Verify ethernet_switch_username and ethernet_switch_password are not empty
@@ -128,7 +125,6 @@
     success_msg: "{{ success_msg_ethernet_switch_password }}"
     fail_msg: "{{ fail_msg_ethernet_switch_password }}"
   when: ethernet_switch_support
-  no_log: true
 
 - name: Verify ib_username and ib_password are not empty
   assert:
@@ -164,7 +160,6 @@
     success_msg: "{{ success_msg_ib_password }}"
     fail_msg: "{{ fail_msg_ib_password }}"
   when: ib_switch_support
-  no_log: true
 
 - name: Verify powervault_me4_username and powervault_me4_password are not empty
   assert:
@@ -191,16 +186,22 @@
 - name: Assert powervault_me4_password
   assert:
     that:
-      - powervault_me4_password | length > min_username_length | int - 1
+      - powervault_me4_password | length > min_length | int - 1
       - powervault_me4_password | length < max_length | int + 1
       - '"-" not in powervault_me4_password '
+      - '"," not in powervault_me4_password '
+      - '"." not in powervault_me4_password '
+      - '"<" not in powervault_me4_password '
       - '"\\" not in powervault_me4_password '
       - '"\"" not in powervault_me4_password '
       - " \"'\" not in powervault_me4_password "
+      - powervault_me4_password | regex_search('^(?=.*[a-z]).+$')
+      - powervault_me4_password | regex_search('^(?=.*[A-Z]).+$')
+      - powervault_me4_password | regex_search('^(?=.*\\d).+$')
+      - powervault_me4_password | regex_search('^(?=.*[!#$%&()*+/:;=>?@^_`{} ~]).+$')
     success_msg: "{{ success_msg_powervault_me4_password }}"
     fail_msg: "{{ fail_msg_powervault_me4_password }}"
   when: powervault_support
-  no_log: true
 
 - name: Create ansible vault key
   set_fact:
@@ -221,4 +222,9 @@
   command: >-
     ansible-vault encrypt {{ login_vars_filename }}
     --vault-password-file {{ vault_filename }}
-  changed_when: false
+  changed_when: false
+
+- name: Update login_vars.yml permission
+  file:
+    path: "{{ login_vars_filename }}"
+    mode: "{{ vault_file_perm }}"

+ 5 - 0
control_plane/roles/control_plane_common/tasks/verify_omnia_params.yml

@@ -96,3 +96,8 @@
     ansible-vault encrypt {{ role_path }}/../../../{{ config_filename }}
     --vault-password-file {{ role_path }}/../../../{{ config_vaultname }}
   changed_when: false
+
+- name: Update omnia_config.yml permission
+  file:
+    path: "{{ role_path }}/../../../{{ config_filename }}"
+    mode: "{{ vault_file_perm }}"

+ 11 - 5
control_plane/roles/control_plane_common/vars/main.yml

@@ -55,7 +55,7 @@ min_length: 8
 max_length: 30
 min_username_length: 4
 file_perm: '0755'
-vault_file_perm: '0600'
+vault_file_perm: '0644'
 nic_min_length: 3
 input_config_failure_msg: "Please provide all the required parameters in login_vars.yml"
 fail_msg_provision_password: "Failed. Incorrect provision_password format provided in login_vars.yml"
@@ -80,10 +80,10 @@ success_msg_ib_password: "ib password validated"
 fail_msg_ib_password: "Failed. Incorrect ib_password format provided in base_vars.yml"
 pv_params_success_msg: "Powervault switch username and password are not blank"
 pv_params_empty_fail_msg: "Failed. Powervault username or password cannot be empty when powervault_support is true"
-success_powervault_username: "powervault username validated"
-fail_powervault_username: "Failed. Incorrect powervault_username format provided in base_vars.yml"
-success_msg_powervault_password: "powervault password validated"
-fail_msg_powervault_password: "Failed. Incorrect powervault_password format provided in base_vars.yml"
+success_powervault_me4_username: "powervault username validated"
+fail_powervault_me4_username: "Failed. Incorrect powervault_username format provided in base_vars.yml"
+success_msg_powervault_me4_password: "powervault password validated"
+fail_msg_powervault_me4_password: "Failed. Incorrect powervault_password format provided in base_vars.yml"
 
 # Usage: verify_omnia_params.yml
 config_filename: "omnia_config.yml"
@@ -132,6 +132,8 @@ success_msg_different_nics: "The nics of different containers and public nic are
 fail_msg_different_nics: "Failed. Incorrect nic information. public nic, management network nic and host network nic should not be the same"
 success_msg_different_nics_ib: "The nics of different containers and public nic are not the same as infiniband nic- Validated"
 fail_msg_different_nics_ib: "Failed. Infiniband nic cannot be the same as other nics"
+success_msg_ib: "Infiniband variables validated"
+fail_msg_ib: "Failed. Please provide all the InfiniBand related parameters in base_vars.yml"
 
 # Usage: fetch_sm_inputs.yml
 ib_config_file: "{{ role_path }}/../../input_params/ib_vars.yml"
@@ -143,6 +145,10 @@ fail_msg_opensm_config_file: opensm.conf file doesn't exist.
 fail_msg_ib_input_definition: Infiniband config directories must be defined.
 fail_msg_ib_input: Infiniband config directories can't be left empty.
 
+# Usage: encrypt_idrac_2fa_vars.yml
+idrac_tools_vaultname: input_params/.idrac_vault_key
+idrac_tools_vars_filename: input_params/idrac_tools_vars.yml
+
 # Usage: nfs_server_setup.yml
 nfs_share_offline_repo: /var/nfs_repo
 nfs_share_awx: /var/nfs_awx

+ 2 - 2
control_plane/roles/control_plane_customiso/tasks/create_unattended_iso.yml

@@ -40,8 +40,8 @@
     state: absent
   tags: install
 
-- name: Include control_plane_repo vars
-  include_vars: ../../control_plane_repo/vars/main.yml
+- name: Include control_plane_common vars
+  include_vars: ../../control_plane_common/vars/main.yml
 
 - name: Copy ISO file to nfs share
   copy:

File diff suppressed because it is too large
+ 0 - 1
control_plane/roles/control_plane_repo/files/R430_inv.xml


+ 0 - 1
control_plane/roles/control_plane_repo/files/poweredge_models.txt

@@ -3,7 +3,6 @@ C6420
 C6520
 R240
 R340
-R430
 R440
 R540
 R640

+ 137 - 142
control_plane/roles/provision_idrac/tasks/check_prerequisites.yml

@@ -13,8 +13,8 @@
 # limitations under the License.
 ---
 
-- name: Include control_plane_repo vars
-  include_vars: ../../control_plane_repo/vars/main.yml
+- name: Include control_plane_common vars
+  include_vars: ../../control_plane_common/vars/main.yml
   run_once: true
 
 - name: Include custom_iso vars
@@ -26,7 +26,7 @@
     path: "{{ role_path }}/files/{{ management_station_ip_file }}"
   register: ip_file_check
   run_once: true
-
+  
 - name: Fetch management station ip from {{ management_station_ip_file }}
   shell: cat {{ role_path }}/files/{{ management_station_ip_file }}
   changed_when: false
@@ -38,11 +38,11 @@
   fail:
     msg: "{{ missing_ip_file_fail_msg }}"
   when: not ip_file_check.stat.exists
-
+  
 - name: Set management_station_ip
   set_fact:
     management_station_ip: "{{ fetch_ip.stdout }}"
-
+    
 - name: Check NFS share access
   idrac_server_config_profile:
     idrac_ip: "{{ inventory_hostname }}"
@@ -75,143 +75,138 @@
     datacenter_license: false
     provision_status: false
 
-- name: Check tower_cli.cfg is encrypted
-  command: cat "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
-  changed_when: false
-  register: awx_content
-  run_once: true
-  when: awx_search_key in hostname.stdout
-
-- name: Decrpyt tower_cli.cfg
-  command: >-
-    ansible-vault decrypt "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
-    --vault-password-file "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vaultname }}"
-  changed_when: false
-  run_once: true
-  when:
-    - awx_search_key in hostname.stdout
-    - "'$ANSIBLE_VAULT;' in awx_content.stdout"
+- block:
+    - name: Check tower_cli.cfg is encrypted
+      command: cat "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
+      changed_when: false
+      register: awx_content
+      run_once: true
+
+    - name: Decrpyt tower_cli.cfg
+      command: >-
+        ansible-vault decrypt "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
+        --vault-password-file "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vaultname }}"
+      changed_when: false
+      run_once: true
+      when: "'$ANSIBLE_VAULT;' in awx_content.stdout"
+
+    - name: Fetch awx host
+      command: grep "host:" "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
+      register: fetch_awx_host
+      changed_when: false
+      run_once: true
+
+    - name: Fetch awx username
+      command: grep "username:" "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
+      register: fetch_awx_username
+      changed_when: false
+      run_once: true
+      no_log: true
+
+    - name: Fetch awx password
+      command: grep "password:" "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
+      register: fetch_awx_password
+      changed_when: false
+      run_once: true
+      no_log: true
+
+    - name: Set awx variables
+      set_fact:
+        awx_host: "{{ fetch_awx_host.stdout | regex_replace('host: ','') }}"
+        awx_username: "{{ fetch_awx_username.stdout | regex_replace('username: ','') }}"
+        awx_password: "{{ fetch_awx_password.stdout | regex_replace('password: ','') }}"
+      no_log: true
+
+    - name: Encrypt tower_cli.cfg
+      command: >-
+        ansible-vault encrypt "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
+        --vault-password-file "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vaultname }}"
+      changed_when: false
+      run_once: true
+      when: "'$ANSIBLE_VAULT;' in awx_content.stdout"
+      
+    - name: Get inventory list
+      command: >-
+        awx --conf.host "{{ awx_host }}" --conf.username "{{ awx_username }}" --conf.password "{{ awx_password }}"
+        inventory list -f human --filter "name"
+      register: inventory_list
+      run_once: true
+      changed_when: false
+      no_log: true
+
+    - name: Create provisioned_idrac inventory
+      command: >-
+        awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
+        inventory create --name "{{ provisioned_idrac_inventory_name }}" --organization "{{ awx_organization }}"
+      register: create_inventory
+      run_once: true
+      changed_when: true
+      no_log: true
+      when: provisioned_idrac_inventory_name not in inventory_list.stdout
+
+    - name: Fetch provisioned_idrac inventory
+      command: >-
+        awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
+        hosts list --inventory "{{ provisioned_idrac_inventory_name }}" -f human --filter "name"
+      register: fetch_inventory
+      run_once: true
+      changed_when: false
+      no_log: true
+
+    - name: Set provision status
+      set_fact:
+        provision_status: true
+      when: inventory_hostname in fetch_inventory.stdout
+
+    - name: Removing hosts already provisioned
+      debug:
+        msg: "{{ provision_skip_msg }}"
+      when: provision_status
 
-- name: Fetch awx host
-  command: grep "host:" "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
-  register: fetch_awx_host
-  changed_when: false
-  run_once: true
   when: awx_search_key in hostname.stdout
 
-- name: Fetch awx username
-  command: grep "username:" "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
-  register: fetch_awx_username
-  changed_when: false
-  run_once: true
-  no_log: true
-  when: awx_search_key in hostname.stdout
-
-- name: Fetch awx password
-  command: grep "password:" "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
-  register: fetch_awx_password
-  changed_when: false
-  run_once: true
-  no_log: true
-  when: awx_search_key in hostname.stdout
-
-- name: Set awx variables
-  set_fact:
-    awx_host: "{{ fetch_awx_host.stdout | regex_replace('host: ','') }}"
-    awx_username: "{{ fetch_awx_username.stdout | regex_replace('username: ','') }}"
-    awx_password: "{{ fetch_awx_password.stdout | regex_replace('password: ','') }}"
-  no_log: true
-  when: awx_search_key in hostname.stdout
-
-- name: Encrypt tower_cli.cfg
-  command: >-
-    ansible-vault encrypt "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vars_filename }}"
-    --vault-password-file "{{ playbook_dir }}/roles/webui_awx/files/{{ awx_vaultname }}"
-  changed_when: false
-  run_once: true
-  when:
-    - awx_search_key in hostname.stdout
-    - "'$ANSIBLE_VAULT;' in awx_content.stdout"
-
-- name: Get inventory list
-  command: >-
-     awx --conf.host "{{ awx_host }}" --conf.username "{{ awx_username }}" --conf.password "{{ awx_password }}"
-     inventory list -f human --filter "name"
-  register: inventory_list
-  run_once: true
-  changed_when: false
-  no_log: true
-  when: awx_search_key in hostname.stdout
-
-- name: Create provisioned_idrac inventory
-  command: >-
-    awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
-    inventory create --name "{{ provisioned_idrac_inventory_name }}" --organization "{{ awx_organization }}"
-  register: create_inventory
-  run_once: true
-  changed_when: true
-  no_log: true
-  when:
-    - awx_search_key in hostname.stdout
-    - provisioned_idrac_inventory_name not in inventory_list.stdout
-
-- name: Fetch provisioned_idrac inventory
-  command: >-
-    awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
-    hosts list --inventory "{{ provisioned_idrac_inventory_name }}" -f human --filter "name"
-  register: fetch_inventory
-  run_once: true
-  changed_when: false
-  no_log: true
-  when: awx_search_key in hostname.stdout
-
-- name: Removing hosts already provisioned
-  fail:
-    msg: "{{ provision_skip_msg }}"
-  when:
-    - awx_search_key in hostname.stdout
-    - inventory_hostname in fetch_inventory.stdout
-
-- name: Show status of the Lifecycle Controller
-  dellemc.openmanage.idrac_lifecycle_controller_status_info:
-    idrac_ip: "{{ inventory_hostname }}"
-    idrac_user: "{{ idrac_username }}"
-    idrac_password: "{{ idrac_password }}"
-  register: lc_check_status
-
-- name: LC not available
-  fail:
-    msg: "{{ lc_check_fail_msg }}"
-  when: not lc_check_status.lc_status_info.LCReady
-  register: lc_fail
-
-- name: Get system inventory
-  dellemc.openmanage.idrac_system_info:
-    idrac_ip: "{{ inventory_hostname }}"
-    idrac_user: "{{ idrac_username }}"
-    idrac_password: "{{ idrac_password }}"
-  register: idrac_info
-
-- name: Set enterprise license status
-  set_fact:
-    enterprise_license: true
-  with_items: "{{ idrac_info.system_info.License }}"
-  when:
-    - '"iDRAC" in idrac_info.system_info.License[my_idx1].LicenseDescription'
-    - '"Enterprise" in idrac_info.system_info.License[my_idx1].LicenseDescription'
-    - '"License" in idrac_info.system_info.License[my_idx1].LicenseDescription'
-    - '"Healthy" in idrac_info.system_info.License[my_idx1].PrimaryStatus'
-  loop_control:
-    index_var: my_idx1
-
-- name: Set datacenter license status
-  set_fact:
-    datacenter_license: true
-  with_items: "{{ idrac_info.system_info.License }}"
-  when:
-    - '"iDRAC" in idrac_info.system_info.License[my_idx2].LicenseDescription'
-    - '"Datacenter" in idrac_info.system_info.License[my_idx2].LicenseDescription'
-    - '"License" in idrac_info.system_info.License[my_idx2].LicenseDescription'
-    - '"Healthy" in idrac_info.system_info.License[my_idx2].PrimaryStatus'
-  loop_control:
-    index_var: my_idx2
+- block:
+    - name: Show status of the Lifecycle Controller
+      dellemc.openmanage.idrac_lifecycle_controller_status_info:
+        idrac_ip: "{{ inventory_hostname }}"
+        idrac_user: "{{ idrac_username }}"
+        idrac_password: "{{ idrac_password }}"
+      register: lc_check_status
+
+    - name: LC not available
+      fail:
+        msg: "{{ lc_check_fail_msg }}"
+      when: not lc_check_status.lc_status_info.LCReady
+      register: lc_fail
+
+    - name: Get system inventory
+      dellemc.openmanage.idrac_system_info:
+        idrac_ip: "{{ inventory_hostname }}"
+        idrac_user: "{{ idrac_username }}"
+        idrac_password: "{{ idrac_password }}"
+      register: idrac_info
+
+    - name: Set enterprise license status
+      set_fact:
+        enterprise_license: true
+      with_items: "{{ idrac_info.system_info.License }}"
+      when:
+        - '"iDRAC" in idrac_info.system_info.License[my_idx1].LicenseDescription'
+        - '"Enterprise" in idrac_info.system_info.License[my_idx1].LicenseDescription'
+        - '"License" in idrac_info.system_info.License[my_idx1].LicenseDescription'
+        - '"Healthy" in idrac_info.system_info.License[my_idx1].PrimaryStatus'
+      loop_control:
+        index_var: my_idx1
+
+    - name: Set datacenter license status
+      set_fact:
+        datacenter_license: true
+      with_items: "{{ idrac_info.system_info.License }}"
+      when:
+        - '"iDRAC" in idrac_info.system_info.License[my_idx2].LicenseDescription'
+        - '"Datacenter" in idrac_info.system_info.License[my_idx2].LicenseDescription'
+        - '"License" in idrac_info.system_info.License[my_idx2].LicenseDescription'
+        - '"Healthy" in idrac_info.system_info.License[my_idx2].PrimaryStatus'
+      loop_control:
+        index_var: my_idx2
+  when: not provision_status

+ 7 - 2
control_plane/roles/provision_idrac/tasks/main.yml

@@ -23,13 +23,18 @@
 
 - name: Update firmware
   include_tasks: update_firmware.yml
-  when: firmware_update_required
+  when:
+    - not provision_status
+    - firmware_update_required
 
 - name: Import SCP
   include_tasks: import_scp.yml
+  when: not provision_status
 
 - name: Create VD
   include_tasks: create_vd.yml
+  when: not provision_status
 
 - name: Deploy OS
-  include_tasks: deploy_os.yml
+  include_tasks: deploy_os.yml
+  when: not provision_status

+ 41 - 20
control_plane/roles/provision_idrac/tasks/update_firmware.yml

@@ -13,25 +13,46 @@
 # limitations under the License.
 ---
 
-- name: Update firmware (This task will take time based on current firmware version)
-  dellemc.openmanage.idrac_firmware:
-    idrac_ip: "{{ inventory_hostname }}"
-    idrac_user: "{{ idrac_username }}"
-    idrac_password: "{{ idrac_password }}"
-    share_name: "{{ management_station_ip }}:{{ nfs_share_offline_repo }}/dellupdates"
-    reboot: True
-    job_wait: True
-    apply_update: True
-    catalog_file_name: "Catalog.xml"
-  register: update_firmware
+- block:
+    - name: Update firmware (This task will take time based on current firmware version)
+      dellemc.openmanage.idrac_firmware:
+        idrac_ip: "{{ inventory_hostname }}"
+        idrac_user: "{{ idrac_username }}"
+        idrac_password: "{{ idrac_password }}"
+        share_name: "{{ management_station_ip }}:{{ nfs_share_offline_repo }}/dellupdates"
+        reboot: True
+        job_wait: True
+        apply_update: True
+        catalog_file_name: "Catalog.xml"
+      register: update_firmware
 
-- name: Wait for one minute to complete firmware update
-  wait_for:
-    timeout: 60 
-  run_once: true
+    - name: Wait for one minute to complete firmware update
+      wait_for:
+        timeout: 60
+      run_once: true
 
-- name: Wait for iDRAC port to become active
-  wait_for:
-    host: "{{ inventory_hostname }}"
-    port: "{{ idrac_port }}"
-    state: started
+    - name: Wait for iDRAC port to become active
+      wait_for:
+        host: "{{ inventory_hostname }}"
+        port: "{{ idrac_port }}"
+        state: started
+
+    - name: Firmware update status
+      debug:
+        msg: "{{ firmware_job_success_msg }}"
+      when: not update_firmware.failed
+
+  rescue:
+    - name: Firmware update job status
+      debug:
+        msg: "{{ update_firmware.msg }}"
+      when:
+        - update_firmware.failed
+        - idrac_error_message in update_firmware.msg
+
+    - name: Firmware update job failed
+      fail:
+        msg: "{{ firmware_job_fail_msg }}, Error: {{ update_firmware.msg }}"
+      when:
+        - update_firmware.failed
+        - idrac_error_message not in update_firmware.msg

+ 1 - 0
control_plane/roles/provision_idrac/tasks/validate_idrac_vars.yml

@@ -24,6 +24,7 @@
 - name: Initialize variables
   set_fact:
     snmp_trap_status: false
+    provision_status: false
   run_once: true
 
 - name: Validate input parameters are not empty

+ 3 - 0
control_plane/roles/provision_idrac/vars/main.yml

@@ -46,6 +46,9 @@ awx_vaultname: ".tower_vault_key"
 
 # Usage: update_firmware.yml
 idrac_port: 443
+idrac_error_message: "Unable to complete the operation because the catalog name entered has either unsupported firmware packages or same version installed on the server"
+firmware_job_fail_msg: "Failed. Error occured while updating firmware"
+firmware_job_success_msg: "Firmware update job compeleted successfully"
 
 # Usage: import_scp.yml
 scp_filename: idrac_scp.xml

+ 21 - 0
control_plane/tools/configure_new_devices.yml

@@ -0,0 +1,21 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Configure new idrac server/ethernet switches
+  hosts: all
+  connection: local
+  gather_facts: false
+  roles:
+    - configure_new_devices

+ 21 - 0
control_plane/tools/idrac_2fa.yml

@@ -0,0 +1,21 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Enable two factor authentication in idrac servers
+  hosts: all
+  connection: local
+  gather_facts: false
+  roles:
+    - idrac_2fa

+ 21 - 0
control_plane/tools/idrac_ldap.yml

@@ -0,0 +1,21 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Enable/Disable LDAP in idrac servers
+  hosts: all
+  connection: local
+  gather_facts: false
+  roles:
+    - idrac_ldap

+ 104 - 0
control_plane/tools/roles/configure_new_devices/tasks/main.yml

@@ -0,0 +1,104 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Include provision_idrac vars
+  include_vars: "{{ playbook_dir }}/../roles/provision_idrac/vars/main.yml"
+  run_once: true
+
+- name: Check tower_cli.cfg is encrypted
+  command: cat "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vars_filename }}"
+  changed_when: false
+  register: awx_content
+  run_once: true
+
+- name: Decrpyt tower_cli.cfg
+  command: >-
+    ansible-vault decrypt "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vars_filename }}"
+    --vault-password-file "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vaultname }}"
+  changed_when: false
+  run_once: true
+  when: "'$ANSIBLE_VAULT;' in awx_content.stdout"
+
+- name: Fetch awx host
+  command: grep "host:" "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vars_filename }}"
+  register: fetch_awx_host
+  changed_when: false
+  run_once: true
+
+- name: Fetch awx username
+  command: grep "username:" "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vars_filename }}"
+  register: fetch_awx_username
+  changed_when: false
+  run_once: true
+  no_log: true
+
+- name: Fetch awx password
+  command: grep "password:" "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vars_filename }}"
+  register: fetch_awx_password
+  changed_when: false
+  run_once: true
+  no_log: true
+
+- name: Set awx variables
+  set_fact:
+    awx_host: "{{ fetch_awx_host.stdout | regex_replace('host: ','') }}"
+    awx_username: "{{ fetch_awx_username.stdout | regex_replace('username: ','') }}"
+    awx_password: "{{ fetch_awx_password.stdout | regex_replace('password: ','') }}"
+  no_log: true
+
+- name: Encrypt tower_cli.cfg
+  command: >-
+    ansible-vault encrypt "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vars_filename }}"
+    --vault-password-file "{{ playbook_dir }}/../roles/webui_awx/files/{{ awx_vaultname }}"
+  changed_when: false
+  run_once: true
+  when: "'$ANSIBLE_VAULT;' in awx_content.stdout"
+
+- name: Get inventory list
+  command: >-
+    awx --conf.host "{{ awx_host }}" --conf.username "{{ awx_username }}" --conf.password "{{ awx_password }}"
+    inventory list -f human --filter "name"
+  register: inventory_list
+  run_once: true
+  changed_when: false
+  no_log: true
+
+- block:
+    - name: Fetch provisioned_idrac inventory
+      command: >-
+        awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
+        hosts list --inventory "{{ provisioned_idrac_inventory_name }}" -f human --filter "name"
+      register: fetch_inventory
+      run_once: true
+      changed_when: false
+      no_log: true
+    
+    - name: Launch ethernet_template
+      command: >-
+        awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
+        job_templates launch {{ ethernet_template_name }} --wait
+      run_once: true
+      changed_when: true
+      no_log: true
+
+    - name: Launch idrac_template
+      command: >-
+        awx --conf.host {{ awx_host }} --conf.username {{ awx_username }} --conf.password {{ awx_password }}
+        job_templates launch {{ idrac_template_name }} --wait
+      run_once: true
+      changed_when: false
+      no_log: true
+      when: inventory_hostname not in fetch_inventory.stdout
+  when: provisioned_idrac_inventory_name in inventory_list.stdout

+ 20 - 0
control_plane/tools/roles/configure_new_devices/vars/main.yml

@@ -0,0 +1,20 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+# vars file for configure_new_devices role
+
+# Usage: main.yml
+ethernet_template_name: "ethernet_template"
+idrac_template_name: "idrac_template"

+ 54 - 0
control_plane/tools/roles/idrac_2fa/tasks/configure_2fa.yml

@@ -0,0 +1,54 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Fetch idrac_username index
+  community.general.idrac_redfish_info:
+    category: Manager
+    command: GetManagerAttributes
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"
+  register: idrac_manager_info
+
+- name: Set idrac_attributes
+  set_fact:
+    idrac_attributes: "{{ idrac_manager_info.redfish_facts.entries | selectattr('Id', 'defined') | selectattr('Id', 'equalto', 'iDRACAttributes') }}"
+
+- name: Enable 2FA
+  community.general.idrac_redfish_config:
+    category: Manager
+    command: SetManagerAttributes
+    resource_id: iDRAC.Embedded.1
+    manager_attributes:
+      Users.2.EmailAddress: "{{ use_email_address_2fa }}"
+      Users.2.Simple2FA: "Enabled"
+      Users.2.UseEmail: "Enabled"
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"
+  register: enable_2fa
+  when: idrac_attributes[0].Attributes['Users.2.UserName'] == idrac_username
+
+- name: 2FA skip status
+  debug:
+    msg: "{{ enable2fa_skip_msg }}"
+  when: idrac_attributes[0].Attributes['Users.2.UserName'] != idrac_username
+
+- name: 2FA status
+  debug:
+    msg: "{{ enable_2fa_msg }}"
+  when: 
+    - idrac_attributes[0].Attributes['Users.2.UserName'] == idrac_username
+    - not enable_2fa.failed

+ 77 - 0
control_plane/tools/roles/idrac_2fa/tasks/configure_smtp.yml

@@ -0,0 +1,77 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Configure DNS
+  community.general.idrac_redfish_config:
+    category: Manager
+    command: SetManagerAttributes
+    resource_id: iDRAC.Embedded.1
+    manager_attributes:
+      NIC.1.DNSRegister: "Enabled"
+      NIC.1.DNSDomainName: "{{ dns_domain_name }}"
+      IPv4Static.1.DNS1: "{{ ipv4_static_dns1 }}"
+      IPv4Static.1.DNS2: "{{ ipv4_static_dns2 }}"
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"
+    
+- name: Configure SMTP
+  community.general.idrac_redfish_config:
+    category: Manager
+    command: SetManagerAttributes
+    resource_id: iDRAC.Embedded.1
+    manager_attributes:
+      RemoteHosts.1.SMTPServerIPAddress: "{{ smtp_server_ip }}"
+      RemoteHosts.1.SMTPPort: 25
+      RemoteHosts.1.SMTPAuthentication: "Enabled"
+      RemoteHosts.1.SMTPUserName: "{{ smtp_username }}"
+      RemoteHosts.1.SMTPPassword: "{{ smtp_password }}"
+      EmailAlert.1.Address: "{{ use_email_address_2fa }}"
+      EmailAlert.1.Enable: "Enabled"
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"
+    
+- name: Wait for one minute
+  pause:
+    minutes: 1
+  run_once: true
+
+- name: Sent SMTP test email
+  command: sshpass -p {{ idrac_password }} ssh -o "StrictHostKeyChecking no" {{ idrac_username }}@{{ inventory_hostname }} 'racadm testemail -i 1'
+  changed_when: false
+  register: test_email_check
+  retries: 5
+  no_log: true
+  ignore_errors: true
+  until: not test_email_check.failed
+
+- name: Check test email status
+  assert:
+    that: test_email_success_msg in test_email_check.stdout
+    fail_msg: "{{ test_email_check_fail_msg }}"
+    success_msg: "{{ test_email_check_success_msg }}"
+
+- name: Disable email alert
+  community.general.idrac_redfish_config:
+    category: Manager
+    command: SetManagerAttributes
+    resource_id: iDRAC.Embedded.1
+    manager_attributes:
+      EmailAlert.1.Address: ""
+      EmailAlert.1.Enable: "Disabled"
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"

+ 28 - 0
control_plane/tools/roles/idrac_2fa/tasks/main.yml

@@ -0,0 +1,28 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Validate 2FA vars
+  include_tasks: validate_2fa_vars.yml
+
+- name: Check prerequisites
+  include_tasks: ./../idrac_system_lockdown/tasks/check_prerequisites.yml
+
+- name: Configure SMTP
+  include_tasks: configure_smtp.yml
+  when: two_factor_authentication == "enabled"
+
+- name: Configure 2FA
+  include_tasks: configure_2fa.yml
+  when: two_factor_authentication == "enabled"

+ 96 - 0
control_plane/tools/roles/idrac_2fa/tasks/validate_2fa_vars.yml

@@ -0,0 +1,96 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Include provision_idrac vars
+  include_vars: "{{ playbook_dir }}/../roles/provision_idrac/vars/main.yml"
+  run_once: true
+
+- name: Include control_plane_common vars
+  include_vars: "{{ playbook_dir }}/../roles/control_plane_common/vars/main.yml"
+  run_once: true
+
+- name: Include idrac_vars.yml
+  include_vars: "{{ playbook_dir }}/../{{ idrac_input_filename }}"
+  run_once: true
+
+- name: Warning - waiting for one minute
+  pause:
+    minutes: 1
+    prompt: "{{ enable_2fa_warning_msg }}"
+  run_once: true
+
+- name: Set two_factor_authentication in lowercase
+  set_fact:
+    two_factor_authentication: "{{ two_factor_authentication | lower }}"
+
+- name: Assert two_factor_authentication value
+  assert:
+    that:
+      - two_factor_authentication | length > 1
+      - two_factor_authentication == "enabled" or two_factor_authentication == "disabled"
+    success_msg: "{{ simple_2fa_success_msg }}"
+    fail_msg: "{{ simple_2fa_fail_msg }}"
+
+- name: Disable 2FA message
+  fail:
+    msg: "{{ idrac_2fa_disable_msg }}"
+  when: two_factor_authentication == "disabled"
+
+- block:
+    - name: Check idrac_tools_vars.yml file is encrypted
+      command: cat "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+      changed_when: false
+      run_once: true
+      register: config_content
+    
+    - name: Decrpyt idrac_tools_vars.yml
+      command: >-
+        ansible-vault decrypt "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+        --vault-password-file "{{ playbook_dir }}/../{{ idrac_tools_vaultname }}"
+      changed_when: false
+      run_once: true
+      when: "'$ANSIBLE_VAULT;' in config_content.stdout"
+    
+    - name: Include variable file idrac_tools_vars.yml
+      include_vars: "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+      run_once: true
+      no_log: true
+    
+    - name: Validate SMTP parameters are not empty
+      fail:
+        msg: "{{ smtp_input_fail_msg }}"
+      when:
+        - dns_domain_name | length < 1 or
+          ipv4_static_dns1 | length < 1 or
+          ipv4_static_dns2 | length < 1 or
+          smtp_server_ip | length < 1 or
+          smtp_username | length < 1 or
+          smtp_password | length < 1 or
+          use_email_address_2fa | length < 1 
+
+    - name: Assert use_email_address_2fa value
+      assert:
+        that: '"@" in use_email_address_2fa'
+        success_msg: "{{ email_address_success_msg }}"
+        fail_msg: "{{ email_address_fail_msg }}"
+
+    - name: Encrypt idrac_tools_vars.yml
+      command: >-
+        ansible-vault encrypt "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+        --vault-password-file "{{ playbook_dir }}/../{{ idrac_tools_vaultname }}"
+      changed_when: false
+      run_once: true
+      when: "'$ANSIBLE_VAULT;' in config_content.stdout"
+  when: two_factor_authentication == "enabled"

+ 34 - 0
control_plane/tools/roles/idrac_2fa/vars/main.yml

@@ -0,0 +1,34 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+# vars file for idrac_2fa role
+
+# Usage: validate_2fa_vars.yml
+simple_2fa_success_msg: "two_factor_authentication validated"
+simple_2fa_fail_msg: "Failed. two_factor_authentication accepts only enabled or disabled in idrac_vars.yml"
+smtp_input_fail_msg: "Failed. Please provide all the required parameters in idrac_tools_vars.yml"
+enable_2fa_warning_msg: "[WARNING] Once 2FA is enabled, user has to disable 2FA manually. Other iDRAC playbooks won't run if 2FA is enabled. Interrupt the playbook execution to cancel the task"
+idrac_2fa_disable_msg: "Failed. Disabling 2FA not supported. Disable 2FA manually."
+email_address_success_msg: "use_email_address_2fa validated"
+email_address_fail_msg: "Failed. Invalid format of email address for use_email_address_2fa in idrac_tools_vars.yml"
+
+# Usage: configure_smtp.yml
+test_email_success_msg: "Test email sent successfully"
+test_email_check_success_msg: "Sucessfully sent test email and verified SMTP configuration"
+test_email_check_fail_msg: "Failed. Unable to sent test email. Verify SMTP related input provided in idrac_tools_vars.yml"
+
+# Usage: configure_2fa.yml
+enable2fa_skip_msg: "2FA enable task skipped. 2FA can be enabled only to user index 2"
+enable_2fa_msg: "2FA enabled successfully. For running other iDRAC playbooks, disable 2FA manually first"

+ 60 - 0
control_plane/tools/roles/idrac_ldap/tasks/configure_ldap.yml

@@ -0,0 +1,60 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Enable LDAP
+  community.general.idrac_redfish_config:
+    category: Manager
+    command: SetManagerAttributes
+    resource_id: iDRAC.Embedded.1
+    manager_attributes:
+      LDAP.1.Enable: "Enabled"
+      LDAP.1.Server: "{{ ldap_server_address }}"
+      LDAP.1.BaseDN: "{{ base_dn }}"
+      LDAP.1.UserAttribute: "{{ user_attribute }}"
+      LDAP.1.GroupAttribute: "{{ group_attribute }}"
+      LDAP.1.GroupAttributeIsDN: "{{ group_attribute_is_dn }}"
+      LDAP.1.BindDN: "{{ bind_dn }}"
+      LDAP.1.BindPassword: "{{ bind_password }}"
+      LDAP.1.SearchFilter: "{{ search_filter }}"
+      LDAP.1.CertValidationEnable: "{{ cert_validation_enable }}"
+      LDAPRoleGroup.1.DN: "{{ role_group1_dn }}"
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"
+  register: enable_ldap
+  no_log: true
+  when: ldap_directory_services == "enabled"
+
+- name: Update LDAP port and role group 1 privilage
+  command: sshpass -p {{ idrac_password }} ssh -o "StrictHostKeyChecking no" {{ idrac_username }}@{{ inventory_hostname }} 'racadm set {{ item.name }} {{ item.value }}'
+  changed_when: true
+  no_log: true
+  when: ldap_directory_services == "enabled"
+  with_items:
+    - { name: "iDRAC.LDAP.Port", value: "{{ ldap_port }}" }
+    - { name: "iDRAC.LDAPRoleGroup.1.Privilege", value: "{{ role_group1_privilege_id }}" } 
+          
+- name: Disable LDAP
+  community.general.idrac_redfish_config:
+    category: Manager
+    command: SetManagerAttributes
+    resource_id: iDRAC.Embedded.1
+    manager_attributes:
+      LDAP.1.Enable: "Disabled"
+    baseuri: "{{ inventory_hostname }}"
+    username: "{{ idrac_username }}"
+    password: "{{ idrac_password }}"
+  register: disable_ldap
+  when: ldap_directory_services == "disabled"

+ 23 - 0
control_plane/tools/roles/idrac_ldap/tasks/main.yml

@@ -0,0 +1,23 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Validate LDAP vars
+  include_tasks: validate_ldap_vars.yml
+
+- name: Check prerequisites
+  include_tasks: ./../idrac_system_lockdown/tasks/check_prerequisites.yml
+
+- name: Configure LDAP
+  include_tasks: configure_ldap.yml

+ 141 - 0
control_plane/tools/roles/idrac_ldap/tasks/validate_ldap_vars.yml

@@ -0,0 +1,141 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+- name: Include provision_idrac vars
+  include_vars: "{{ playbook_dir }}/../roles/provision_idrac/vars/main.yml"
+  run_once: true
+
+- name: Include control_plane_common vars
+  include_vars: "{{ playbook_dir }}/../roles/control_plane_common/vars/main.yml"
+  run_once: true
+
+- name: Include idrac_vars.yml
+  include_vars: "{{ playbook_dir }}/../{{ idrac_input_filename }}"
+  run_once: true
+
+- name: Set ldap_directory_services in lowercase
+  set_fact:
+    ldap_directory_services: "{{ ldap_directory_services | lower }}"
+
+- name: Assert ldap_directory_services value
+  assert:
+    that:
+      - ldap_directory_services | length > 1
+      - ldap_directory_services == "enabled" or ldap_directory_services == "disabled"
+    success_msg: "{{ ldap_success_msg }}"
+    fail_msg: "{{ ldap_fail_msg }}"
+
+- block:
+    - name: Check idrac_tools_vars.yml file is encrypted
+      command: cat "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+      changed_when: false
+      run_once: true
+      register: config_content
+    
+    - name: Decrpyt idrac_tools_vars.yml
+      command: >-
+        ansible-vault decrypt "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+        --vault-password-file "{{ playbook_dir }}/../{{ idrac_tools_vaultname }}"
+      changed_when: false
+      run_once: true
+      when: "'$ANSIBLE_VAULT;' in config_content.stdout"
+    
+    - name: Include variable file idrac_tools_vars.yml
+      include_vars: "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+      run_once: true
+      no_log: true
+    
+    - name: Validate LDAP parameters are not empty
+      fail:
+        msg: "{{ ldap_input_fail_msg }}"
+      when:
+        - cert_validation_enable | length < 1 or
+          ldap_server_address | length < 1 or
+          ldap_port | length < 1 or
+          base_dn | length < 1 or
+          group_attribute_is_dn | length < 1 or
+          role_group1_dn | length < 1 or
+          role_group1_privilege | length < 1
+    
+    - name: Set ldap_directory_services in lowercase
+      set_fact:
+        cert_validation_enable: "{{ cert_validation_enable | lower }}"
+        group_attribute_is_dn: "{{ group_attribute_is_dn | lower }}"
+        base_dn: "{{ base_dn | lower }}"
+        role_group1_dn: "{{ role_group1_dn | lower }}"
+        bind_dn: "{{ bind_dn | lower }}"
+        
+         
+    - name: Assert cert_validation_enable value
+      assert:
+        that: cert_validation_enable == "disabled"
+        success_msg: "{{ cert_validation_success_msg }}"
+        fail_msg: "{{ cert_validation_fail_msg }}"
+
+    - name: Assert bind_dn value
+      assert:
+        that: 
+          - '"dc=" in bind_dn'
+          - '"ou="  in bind_dn'
+        success_msg: "{{ bind_dn_success_msg }}"
+        fail_msg: "{{ bind_dn_fail_msg }}"
+      when: bind_dn | length > 1
+
+    - name: Assert base_dn value
+      assert:
+        that: '"dc=" in base_dn'
+        success_msg: "{{ base_dn_success_msg }}"
+        fail_msg: "{{ base_dn_fail_msg }}"
+
+    - name: Assert group_attribute_is_dn value
+      assert:
+        that: group_attribute_is_dn == "disabled" or group_attribute_is_dn == "enabled"
+        success_msg: "{{ group_attribute_success_msg }}"
+        fail_msg: "{{ group_attribute_fail_msg }}"
+
+    - name: Assert role_group1_dn value
+      assert:
+        that:
+          - '"dc=" in role_group1_dn'
+          - '"ou="  in role_group1_dn'
+        success_msg: "{{ role_group1_dn_success_msg }}"
+        fail_msg: "{{ role_group1_dn_fail_msg }}"
+
+    - name: Assert role_group1_privilege value
+      assert:
+        that: 
+          - role_group1_privilege == "Administrator" or
+            role_group1_privilege == "Operator"  or
+            role_group1_privilege == "ReadOnly"
+        success_msg: "{{ role_group1_privilege_success_msg }}"
+        fail_msg: "{{ role_group1_privilege_fail_msg }}"
+     
+    - name: Set role_group1_privilege value
+      set_fact:
+        role_group1_privilege_id: "{{ item.value }}"
+      when: item.name == role_group1_privilege
+      with_items:
+        - { name: "Administrator", value: "511" }
+        - { name: "Operator", value: "499" }
+        - { name: "ReadOnly", value: "1" }
+
+    - name: Encrypt idrac_tools_vars.yml
+      command: >-
+        ansible-vault encrypt "{{ playbook_dir }}/../{{ idrac_tools_vars_filename }}"
+        --vault-password-file "{{ playbook_dir }}/../{{ idrac_tools_vaultname }}"
+      changed_when: false
+      run_once: true
+      when: "'$ANSIBLE_VAULT;' in config_content.stdout"
+  when: ldap_directory_services == "enabled"

+ 33 - 0
control_plane/tools/roles/idrac_ldap/vars/main.yml

@@ -0,0 +1,33 @@
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+
+# vars file for idrac_ldap role
+
+# Usage: validate_ldap_vars.yml
+ldap_success_msg: "generic_ldap_directory_services validated"
+ldap_fail_msg: "Failed. generic_ldap_directory_services accepts only enabled or disabled in idrac_vars.yml"
+ldap_input_fail_msg: "Failed. Please provide all the required parameters in idrac_tools_vars.yml"
+cert_validation_success_msg: "cert_validation_enable validated"
+cert_validation_fail_msg: "Failed. cert_validation_enable accepts only disabled in idrac_tools_vars.yml"
+bind_dn_success_msg: "bind_dn validated"
+bind_dn_fail_msg: "Failed. Invalid format for bind_dn in idrac_tools_vars.yml"
+base_dn_success_msg: "base_dn validated"
+base_dn_fail_msg: "Failed. Invalid format for base_dn in idrac_tools_vars.yml"
+group_attribute_success_msg: "group_attribute_is_dn is validated"
+group_attribute_fail_msg: "Failed. group_attribute_is_dn accepts only enabled or disabled in idrac_tools_vars.yml"
+role_group1_dn_success_msg: "role_group1_dn validated"
+role_group1_dn_fail_msg: "Failed. Invalid format for role_group1_dn in idrac_tools_vars.yml"
+role_group1_privilege_success_msg: "role_group1_privilege validated"
+role_group1_privilege_fail_msg: "Failed. role_group1_privilege accepts only Administrator or Operator or ReadOnly in idrac_tools_vars.yml"

+ 4 - 1
control_plane/tools/roles/idrac_secure_boot/tasks/configure_secure_boot.yml

@@ -13,6 +13,10 @@
 # limitations under the License.
 ---
 
+- name: Set uefi_secure_boot in lowercase
+  set_fact:
+    uefi_secure_boot: "{{ uefi_secure_boot | lower }}"
+
 - name: Assert uefi_secure_boot value
   assert:
     that:
@@ -20,7 +24,6 @@
       - uefi_secure_boot == "enabled" or uefi_secure_boot == "disabled"
     success_msg: "{{ secure_boot_success_msg }}"
     fail_msg: "{{ secure_boot_fail_msg }}"
-  run_once: true
 
 - name: Enable secure boot
   dellemc.openmanage.idrac_bios:

+ 6 - 2
control_plane/tools/roles/idrac_system_lockdown/tasks/check_prerequisites.yml

@@ -17,6 +17,10 @@
   include_vars: "{{ playbook_dir }}/../roles/provision_idrac/vars/main.yml"
   run_once: true
 
+- name: Include control_plane_common vars
+  include_vars: "{{ playbook_dir }}/../roles/control_plane_common/vars/main.yml"
+  run_once: true
+
 - name: Include idrac_vars.yml
   include_vars: "{{ playbook_dir }}/../{{ idrac_input_filename }}"
   run_once: true
@@ -38,8 +42,8 @@
 - name: Fetch idrac credentials
   include_tasks: "{{ playbook_dir }}/../roles/provision_idrac/tasks/fetch_idrac_credentials.yml"
   vars:
-    login_input_filename: "{{ playbook_dir }}/../input_params/login_vars.yml"
-    login_vault_filename: "{{ playbook_dir }}/../input_params/.login_vault_key"
+    login_input_filename: "{{ playbook_dir }}/../{{ login_vars_filename }}"
+    login_vault_filename: "{{ playbook_dir }}/../{{ vault_filename }}"
   when: '"awx-" not in hostname.stdout'
 
 - name: Show status of the Lifecycle Controller

+ 5 - 2
control_plane/tools/roles/idrac_system_lockdown/tasks/configure_system_lockdown.yml

@@ -13,6 +13,10 @@
 # limitations under the License.
 ---
 
+- name: Set system_lockdown in lowercase
+  set_fact:
+    system_lockdown: "{{ system_lockdown | lower }}"
+
 - name: Assert system_lockdown value
   assert:
     that:
@@ -20,8 +24,7 @@
       - system_lockdown == "enabled" or system_lockdown == "disabled"
     success_msg: "{{ system_lockdown_success_msg }}"
     fail_msg: "{{ system_lockdown_fail_msg }}"
-  run_once: true
-
+    
 - name: Enable system lockdown
   dellemc.openmanage.dellemc_system_lockdown_mode:
     idrac_ip: "{{ inventory_hostname }}"

+ 4 - 0
platforms/roles/kubeflow/tasks/main.yml

@@ -135,3 +135,7 @@
     cmd: "/usr/bin/kfctl apply -V -f '{{ kubeflow_config_file }}'"
     chdir: "{{ omnia_kubeflow_dir_path }}"
   changed_when: true
+  register: apply_kubeflow_config
+  until: apply_kubeflow_config is not failed
+  retries: 20
+  delay: 10