How to manage Windows services with Salt

Suppose, not entirely hypothetically, that you need to manage a Windows service with Salt. You’re grabbing an executable from, I don’t know, say GitHub, and you want to be able to easily upgrade the service. You might start with something like this (I’m omitting other stuff you need to set up the service):

windows_service_exe_file:
  file.managed:
    - name: "{{ windows_service_path }}{{ windows_service_binary }}"
    - source: "{{ windows_service_exe_download_path }}"
    - source_hash: "{{ windows_service_file_hash }}"

windows_service_running:
  service.running:
    - name: test_service
    - enable: True
    - watch:
      - file: windows_service_exe_file

Great. Now when you update the download path and hash, your service will get restarted.

Except….you can’t replace the file, because Windows is still running the service.  So you need to add a stanza like this to shut down the service if the file is to be replaced:

# this is a pattern to shut down the windows service before the file gets
# replaced.  If you don't do this, you'll get a permission denied because the
# service is still running and using the file.
windows_exporter_service_shutdown:
  service.dead:
    - name: test_service
    - prereq:
      - file: windows_service_exe_file

Now what will happen is that the windows_service_exe_file will know that it needs to be replaced, and that will kick off the service.dead directive in windows_exporter_service_shutdown. Then, the file will be replaced, and the watch in windows_service_running will then restart the service, and the world is happy.