diff --git a/library/kube.py b/library/kube.py
index fa8312f2736c370c47cd94a0f1a454c140160dcb..a84578ff05aeca44d73dc3c548c4106347cac705 100644
--- a/library/kube.py
+++ b/library/kube.py
@@ -288,8 +288,6 @@ def main():
     else:
         module.fail_json(msg='Unrecognized state %s.' % state)
 
-    if result:
-        changed = True
     module.exit_json(changed=changed,
                      msg='success: %s' % (' '.join(result))
                      )
diff --git a/roles/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml
index 45e18391da3e76917f60aca1bd08bf5df75d4250..570947203b8c5cd5f7f38eea8fe0636177dc19dd 100644
--- a/roles/etcd/tasks/main.yml
+++ b/roles/etcd/tasks/main.yml
@@ -16,6 +16,7 @@
 - name: "Gen_certs | Get etcd certificate serials"
   shell: "openssl x509 -in {{ etcd_cert_dir }}/node-{{ inventory_hostname }}.pem -noout -serial | cut -d= -f2"
   register: "etcd_client_cert_serial_result"
+  changed_when: false
   when: inventory_hostname in groups['k8s-cluster']|union(groups['etcd'])|union(groups['calico-rr']|default([]))|unique|sort
 
 - name: Set etcd_client_cert_serial
diff --git a/roles/kubernetes-apps/policy_controller/calico/tasks/main.yml b/roles/kubernetes-apps/policy_controller/calico/tasks/main.yml
index 9a7225186db0750a5249e4c40c4b72ca728c88a0..354a5ad3546eddc043b9f4172067cee4adc52961 100644
--- a/roles/kubernetes-apps/policy_controller/calico/tasks/main.yml
+++ b/roles/kubernetes-apps/policy_controller/calico/tasks/main.yml
@@ -11,6 +11,7 @@
   shell: "{{ bin_dir }}/kubectl -n {{ system_namespace }} get rs calico-policy-controller -o=jsonpath='{$.spec.template.spec.containers[:1].image}' | cut -d':' -f2"
   register: existing_calico_policy_version
   run_once: true
+  changed_when: false
   failed_when: false
 
 # FIXME(mattymo): This should not be necessary
diff --git a/roles/kubernetes/master/tasks/main.yml b/roles/kubernetes/master/tasks/main.yml
index edc788f72d90ff2e4e72afb1acbe6c84accde2f6..d81c2fce9d5e7adb561f08e8df91a8209592b9b9 100644
--- a/roles/kubernetes/master/tasks/main.yml
+++ b/roles/kubernetes/master/tasks/main.yml
@@ -12,10 +12,24 @@
 - include: users-file.yml
   when: kube_basic_auth|default(true)
 
+- name: Compare host kubectl with hyperkube container
+  command: "{{ docker_bin_dir }}/docker run --rm -v {{ bin_dir }}:/systembindir {{ hyperkube_image_repo }}:{{ hyperkube_image_tag }} /usr/bin/cmp /hyperkube /systembindir/kubectl"
+  register: kubectl_task_compare_result
+  until: kubectl_task_compare_result.rc in [0,1,2]
+  retries: 4
+  delay: "{{ retry_stagger | random + 3 }}"
+  changed_when: false
+  failed_when: "kubectl_task_compare_result.rc not in [0,1,2]"
+  tags:
+    - hyperkube
+    - kubectl
+    - upgrade
+
 - name: Copy kubectl from hyperkube container
   command: "{{ docker_bin_dir }}/docker run --rm -v {{ bin_dir }}:/systembindir {{ hyperkube_image_repo }}:{{ hyperkube_image_tag }} /bin/cp /hyperkube /systembindir/kubectl"
-  register: kube_task_result
-  until: kube_task_result.rc == 0
+  when: kubectl_task_compare_result.rc != 0
+  register: kubectl_task_result
+  until: kubectl_task_result.rc == 0
   retries: 4
   delay: "{{ retry_stagger | random + 3 }}"
   changed_when: false
@@ -26,7 +40,7 @@
 
 - name: Install kubectl bash completion
   shell: "{{ bin_dir }}/kubectl completion bash >/etc/bash_completion.d/kubectl.sh"
-  when: ansible_os_family in ["Debian","RedHat"]
+  when: kubectl_task_compare_result.rc != 0 and ansible_os_family in ["Debian","RedHat"]
   tags:
     - kubectl
 
diff --git a/roles/kubernetes/master/tasks/pre-upgrade.yml b/roles/kubernetes/master/tasks/pre-upgrade.yml
index 588db8833292ac792ce90a789032d18a2c26a117..b546f0ca97c98d1ce7eeae9f293686bd3a8d36fd 100644
--- a/roles/kubernetes/master/tasks/pre-upgrade.yml
+++ b/roles/kubernetes/master/tasks/pre-upgrade.yml
@@ -5,6 +5,7 @@
     ETCDCTL_API: 2
   register: old_data_exists
   delegate_to: "{{groups['etcd'][0]}}"
+  changed_when: false
   when: kube_apiserver_storage_backend == "etcd3"
   failed_when: false
 
diff --git a/roles/kubernetes/node/tasks/facts.yml b/roles/kubernetes/node/tasks/facts.yml
index a23e7596c5d9fdcdfaf3fac51459d59b8d8e6d28..41e31c8dc5fb65db7d1243050bb83b8a90f59fb9 100644
--- a/roles/kubernetes/node/tasks/facts.yml
+++ b/roles/kubernetes/node/tasks/facts.yml
@@ -2,6 +2,7 @@
 - name: look up docker cgroup driver
   shell: "docker info | grep 'Cgroup Driver' | awk -F': ' '{ print $2; }'"
   register: docker_cgroup_driver_result
+  changed_when: false
 
 - set_fact:
     standalone_kubelet: >-
diff --git a/roles/kubernetes/node/tasks/pre_upgrade.yml b/roles/kubernetes/node/tasks/pre_upgrade.yml
index ee4836974e35cc9a56f5a20ef0f8c75684c29807..2701808bef1f62cf88e2ef31f59c7bc64437567a 100644
--- a/roles/kubernetes/node/tasks/pre_upgrade.yml
+++ b/roles/kubernetes/node/tasks/pre_upgrade.yml
@@ -8,4 +8,5 @@
 - name: "Pre-upgrade | ensure kubelet container is stopped if using host deployment"
   command: docker stop kubelet
   failed_when: false
+  changed_when: false
   when: kubelet_deployment_type == 'host'
diff --git a/roles/network_plugin/calico/tasks/main.yml b/roles/network_plugin/calico/tasks/main.yml
index 15ab5f660f1d8dbce946226e0014352fe72f369e..7c8e247fccbe09f8487d978457afb312ae47da26 100644
--- a/roles/network_plugin/calico/tasks/main.yml
+++ b/roles/network_plugin/calico/tasks/main.yml
@@ -60,7 +60,7 @@
     - upgrade
 
 - name: Calico | Copy cni plugins from calico/cni container
-  command: "{{ docker_bin_dir }}/docker run --rm -v /opt/cni/bin:/cnibindir {{ calico_cni_image_repo }}:{{ calico_cni_image_tag }} sh -c 'cp -a /opt/cni/bin/* /cnibindir/'"
+  command: "{{ docker_bin_dir }}/docker run --rm -v /opt/cni/bin:/cnibindir {{ calico_cni_image_repo }}:{{ calico_cni_image_tag }} sh -c 'cp /opt/cni/bin/* /cnibindir/'"
   register: cni_task_result
   until: cni_task_result.rc == 0
   retries: 4
diff --git a/roles/network_plugin/canal/tasks/main.yml b/roles/network_plugin/canal/tasks/main.yml
index aaa7c2a169ad5bb1c995e298e014fdc49d76b7cc..812019491edbc9b83016db1805d138f41e3ff227 100644
--- a/roles/network_plugin/canal/tasks/main.yml
+++ b/roles/network_plugin/canal/tasks/main.yml
@@ -30,6 +30,7 @@
     set /{{ cluster_name }}/network/config \
     '{ "Network": "{{ kube_pods_subnet }}", "SubnetLen": {{ kube_network_node_prefix }}, "Backend": { "Type": "{{ flannel_backend_type }}" } }'
   delegate_to: "{{groups['etcd'][0]}}"
+  changed_when: false
   run_once: true
 
 - name: Canal | Create canal node manifests
@@ -61,7 +62,7 @@
     - upgrade
 
 - name: Canal | Copy cni plugins from calico/cni
-  command: "{{ docker_bin_dir }}/docker run --rm -v /opt/cni/bin:/cnibindir {{ calico_cni_image_repo }}:{{ calico_cni_image_tag }} sh -c 'cp -a /opt/cni/bin/* /cnibindir/'"
+  command: "{{ docker_bin_dir }}/docker run --rm -v /opt/cni/bin:/cnibindir {{ calico_cni_image_repo }}:{{ calico_cni_image_tag }} sh -c 'cp /opt/cni/bin/* /cnibindir/'"
   register: cni_task_result
   until: cni_task_result.rc == 0
   retries: 4
@@ -86,7 +87,6 @@
     mode: 0755
     owner: root
     group: root
-  changed_when: false
 
 - name: Canal | Create network policy directory
   file: