【serverspec】WindowsとLinuxを複数台実行

# serverspecでWindowsとLinuxを複数台実行する
serverspecでWindows(WinRM)とLinux(SSH)を複数台実行する際の覚書です。

##環境 
serverspec側：CentOS7.4 / ruby 2.6.
クライアント側：windows server 2019 / CentOS7.4

## ファイル構成
```
(※)はディレクトリで、印がないのはファイル。
├── Rakefile
├── properties.yaml
└── spec(※)
    └── spec_helper.rb
    └── linux(※)
        └──sample_spec.rb
    └── windows(※)
        └──sample_spec.rb
```

## 各ファイル
```
#Rakefile
require 'rake'
require 'rspec/core/rake_task'
require 'yaml'
require 'highline/import'
properties = YAML.load_file('properties.yaml')

ENV['TARGET_USER'] = ''
ENV['TARGET_PASSWORD'] = ''
ENV['TARGET_CONNECTION_METHOD'] = ''
ENV['WINRM_USER'] = 'Windowsのアカウント'
ENV['WINRM_PASSWORD'] = 'Windowsのパスワード'
ENV['SSH_USER'] = 'Linuxのアカウント'
ENV['SSH_PASSWORD'] = 'Linuxのパスワード'

task :spec    => 'spec:all'
task :default => :spec

desc "Run serverspec to all hosts"
task :serverspec => 'serverspec:all'

namespace :serverspec do
  task :all => properties.keys.map {|key| 'serverspec:' + key }
  properties.keys.each do |key|
    desc "Run serverspec to #{key}"
    RSpec::Core::RakeTask.new(key.to_sym) do |t|
      ENV['TARGET_HOST'] = properties[key][:hostname]

      if properties[key][:connection_method] == 'winrm'
        ENV['TARGET_CONNECTION_METHOD'] = 'winrm'
        if properties[key][:user].nil?
          ENV['TARGET_USER'] = ENV['WINRM_USER']
        else
          ENV['TARGET_USER'] = properties[key][:user]
        end
        if properties[key][:password].nil?
          ENV['TARGET_PASSWORD'] = ENV['WINRM_PASSWORD']
        else
          ENV['TARGET_PASSWORD'] = properties[key][:password]
        end

      elsif properties[key][:connection_method] == 'ssh'
        ENV['TARGET_CONNECTION_METHOD'] = 'ssh'
        if properties[key][:user].nil?
          ENV['TARGET_USER'] = ENV['SSH_USER']
        else
          ENV['TARGET_USER'] = properties[key][:user]
        end
        if properties[key][:password].nil?
          ENV['TARGET_PASSWORD'] = ENV['SSH_PASSWORD']
        else
          ENV['TARGET_PASSWORD'] = properties[key][:password]
        end
      end

      t.pattern = 'spec/{' + properties[key][:roles].join(',') + '}/*_spec.rb'
    end
  end
end
```

```
#properties.yaml
windows-server01:
  :roles:
    - windows
  :connection_method: winrm
  :user: ユーザ
  :password: パスワード
  :hostname: IPアドレス

windows-server02:
  :roles:
    - windows
  :connection_method: winrm
  :hostname: IPアドレス #ユーザとパスワードの指定がなければRakefile中の宣言を使用する

centos-server01:
  :roles:
    - linux
  :connection_method: ssh
  :hostname: IPアドレス #ユーザとパスワードの指定がなければRakefile中の宣言を使用する
```

```
#spec_helper.rb
# coding: utf-8

require 'serverspec'
require 'pathname'
require 'winrm'
require 'net/ssh'
require 'yaml'

RSpec.configure do |c|
  puts ''
  puts "#### #{ENV['TARGET_HOST']} ####"
  c.before :all do

    if ENV['TARGET_CONNECTION_METHOD'] == 'winrm'
      set :backend, :winrm
      if Gem::Version.new(WinRM::VERSION) < Gem::Version.new('2')
        winrm = ::WinRM::WinRMWebService.new(endpoint, :ssl, :user => "#{ENV['TARGET_USER']}", :pass => "#{ENV['TARGET_PASSWORD']}", :basic_auth_only => true)
        winrm.set_timeout 300 # 5 minutes max timeout for any operation
      else
        opts = {
          user: "#{ENV['TARGET_USER']}",
          password: "#{ENV['TARGET_PASSWORD']}",
          endpoint: "http://#{ENV['TARGET_HOST']}:5985/wsman",
          operation_timeout: 300,
          no_ssl_peer_verification: false,
        }
      Specinfra.configuration.winrm = ::WinRM::Connection.new(opts)
      end

    elsif ENV['TARGET_CONNECTION_METHOD'] == 'ssh'
      set :backend, :ssh
      set :sudo_password, ENV['TARGET_PASSWORD']
      host = ENV['TARGET_HOST']
      options = Net::SSH::Config.for(host)
      options[:user] ||= ENV['TARGET_USER']
      options[:password] = ENV['TARGET_PASSWORD']
      set :ssh_options, options
      set :host,        options[:host_name] || host
    end
  end
end
```