{"action":"created","comment":{"url":"https://api.github.com/repos/chef/train/pulls/comments/165707669","pull_request_review_id":93693562,"id":165707669,"diff_hunk":"@@ -0,0 +1,108 @@\n+# encoding: utf-8\n+\n+require 'train/plugins'\n+require 'ms_rest_azure'\n+require 'azure_mgmt_resources'\n+require 'inifile'\n+\n+module Train::Transports\n+  class Azure < Train.plugin(1)\n+    name 'azure'\n+    option :tenant_id, default: ENV['AZURE_TENANT_ID']\n+    option :client_id, default: ENV['AZURE_CLIENT_ID']\n+    option :client_secret, default: ENV['AZURE_CLIENT_SECRET']\n+    option :subscription_id, default: ENV['AZURE_SUBSCRIPTION_ID']\n+\n+    # This can provide the client id and secret\n+    option :credentials_file, required: true, default: ENV['AZURE_CRED_FILE']\n+\n+    def connection(_ = nil)\n+      @connection ||= Connection.new(@options)\n+    end\n+\n+    class Connection < BaseConnection\n+      def initialize(options)\n+        # Override for any cli options\n+        # azure://subscription_id\n+        options[:subscription_id] = options[:host] || options[:subscription_id]\n+        super(options)\n+\n+        @cache_enabled[:api_call] = true\n+        @cache[:api_call] = {}\n+\n+        if @options[:client_secret].nil? && @options[:client_id].nil?\n+          parse_credentials_file\n+        end\n+        connect\n+      end\n+\n+      def platform\n+        direct_platform('azure')\n+      end\n+\n+      def azure_client(klass = nil)\n+        # return the managment client by default\n+        klass = ::Azure::Resources::Profiles::Latest::Mgmt::Client if klass.nil?\n+        return klass.new(@credentials) unless cache_enabled?(:api_call)\n+\n+        @cache[:api_call][klass.to_s.to_sym] ||= klass.new(@credentials)\n+      end\n+\n+      def connect\n+        provider = ::MsRestAzure::ApplicationTokenProvider.new(\n+          @options[:tenant_id],\n+          @options[:client_id],\n+          @options[:client_secret],\n+        )\n+\n+        @credentials = {\n+          credentials: ::MsRest::TokenCredentials.new(provider),\n+          subscription_id: @options[:subscription_id],\n+          tenant_id: @options[:tenant_id],\n+          client_id: @options[:client_id],\n+          client_secret: @options[:client_secret],\n+        }\n+      end\n+\n+      def uri\n+        \"azure://#{@options[:subscription_id]}\"\n+      end\n+\n+      private\n+\n+      def parse_credentials_file # rubocop:disable Metrics/AbcSize\n+        # If an INSPEC_AZURE_CREDS environment has been specified set the\n+        # the credentials file to that, otherwise set the one in home\n+        azure_creds_file = @options[:credentials_file]\n+        azure_creds_file = File.join(Dir.home, '.azure', 'credentials') if azure_creds_file.nil?\n+        return unless File.file?(azure_creds_file)\n+\n+        credentials = IniFile.load(File.expand_path(azure_creds_file))\n+        if !@options[:subscription_id].nil?\n+          id = @options[:subscription_id]\n+        elsif !ENV['AZURE_SUBSCRIPTION_NUMBER'].nil?\n+          subscription_number = ENV['AZURE_SUBSCRIPTION_NUMBER'].to_i\n+\n+          # Check that the specified index is not greater than the number of subscriptions\n+          if subscription_number > credentials.sections.length\n+            raise format(\n+              'Your credentials file only contains %s subscriptions.  You specified number %s.',\n+              @credentials.sections.length,\n+              subscription_number,\n+            )\n+          end\n+          id = credentials.sections[subscription_number - 1]\n+        else\n+          raise 'Multiple credentials detected, please set a subscription id to use.' if credentials.sections.count > 1\n+          id = credentials.sections[0]\n+        end\n+\n+        raise format('No credentials found for subscription number %s', id) if credentials.sections.empty? || credentials[id].empty?","path":"lib/train/transports/azure.rb","position":100,"original_position":100,"commit_id":"4403003254a96d5db5095b8c3312a39326d1473e","original_commit_id":"4403003254a96d5db5095b8c3312a39326d1473e","user":{"login":"adamleff","id":3221128,"avatar_url":"https://avatars1.githubusercontent.com/u/3221128?v=4","gravatar_id":"","url":"https://api.github.com/users/adamleff","html_url":"https://github.com/adamleff","followers_url":"https://api.github.com/users/adamleff/followers","following_url":"https://api.github.com/users/adamleff/following{/other_user}","gists_url":"https://api.github.com/users/adamleff/gists{/gist_id}","starred_url":"https://api.github.com/users/adamleff/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adamleff/subscriptions","organizations_url":"https://api.github.com/users/adamleff/orgs","repos_url":"https://api.github.com/users/adamleff/repos","events_url":"https://api.github.com/users/adamleff/events{/privacy}","received_events_url":"https://api.github.com/users/adamleff/received_events","type":"User","site_admin":false},"body":"regular string interpolation would work fine here too, right? We don't use `format` all that often...\r\n\r\n```ruby\r\nraise \"No credentials found for subscription number #{id}\"\r\n```\r\n","created_at":"2018-02-02T17:29:26Z","updated_at":"2018-02-02T17:30:08Z","html_url":"https://github.com/chef/train/pull/250#discussion_r165707669","pull_request_url":"https://api.github.com/repos/chef/train/pulls/250","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/chef/train/pulls/comments/165707669"},"html":{"href":"https://github.com/chef/train/pull/250#discussion_r165707669"},"pull_request":{"href":"https://api.github.com/repos/chef/train/pulls/250"}}},"pull_request":{"url":"https://api.github.com/repos/chef/train/pulls/250","id":166852803,"html_url":"https://github.com/chef/train/pull/250","diff_url":"https://github.com/chef/train/pull/250.diff","patch_url":"https://github.com/chef/train/pull/250.patch","issue_url":"https://api.github.com/repos/chef/train/issues/250","number":250,"state":"open","locked":false,"title":"Add azure transport","user":{"login":"jquick","id":574637,"avatar_url":"https://avatars0.githubusercontent.com/u/574637?v=4","gravatar_id":"","url":"https://api.github.com/users/jquick","html_url":"https://github.com/jquick","followers_url":"https://api.github.com/users/jquick/followers","following_url":"https://api.github.com/users/jquick/following{/other_user}","gists_url":"https://api.github.com/users/jquick/gists{/gist_id}","starred_url":"https://api.github.com/users/jquick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jquick/subscriptions","organizations_url":"https://api.github.com/users/jquick/orgs","repos_url":"https://api.github.com/users/jquick/repos","events_url":"https://api.github.com/users/jquick/events{/privacy}","received_events_url":"https://api.github.com/users/jquick/received_events","type":"User","site_admin":false},"body":"Fixes https://github.com/chef/train/issues/233\r\n\r\nAdds the azure transport and credential parsing. Heavily based off https://github.com/chef/inspec-azure/blob/master/libraries/azure_backend.rb\r\n\r\nSigned-off-by: Jared Quick <jquick@chef.io>","created_at":"2018-02-02T16:58:10Z","updated_at":"2018-02-02T17:30:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f465d86f72b1579bf9ffa9b83810011e26d237d1","assignee":null,"assignees":[],"requested_reviewers":[{"login":"clintoncwolfe","id":156460,"avatar_url":"https://avatars3.githubusercontent.com/u/156460?v=4","gravatar_id":"","url":"https://api.github.com/users/clintoncwolfe","html_url":"https://github.com/clintoncwolfe","followers_url":"https://api.github.com/users/clintoncwolfe/followers","following_url":"https://api.github.com/users/clintoncwolfe/following{/other_user}","gists_url":"https://api.github.com/users/clintoncwolfe/gists{/gist_id}","starred_url":"https://api.github.com/users/clintoncwolfe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/clintoncwolfe/subscriptions","organizations_url":"https://api.github.com/users/clintoncwolfe/orgs","repos_url":"https://api.github.com/users/clintoncwolfe/repos","events_url":"https://api.github.com/users/clintoncwolfe/events{/privacy}","received_events_url":"https://api.github.com/users/clintoncwolfe/received_events","type":"User","site_admin":false}],"requested_teams":[],"milestone":null,"commits_url":"https://api.github.com/repos/chef/train/pulls/250/commits","review_comments_url":"https://api.github.com/repos/chef/train/pulls/250/comments","review_comment_url":"https://api.github.com/repos/chef/train/pulls/comments{/number}","comments_url":"https://api.github.com/repos/chef/train/issues/250/comments","statuses_url":"https://api.github.com/repos/chef/train/statuses/4403003254a96d5db5095b8c3312a39326d1473e","head":{"label":"chef:jq/azure_transport","ref":"jq/azure_transport","sha":"4403003254a96d5db5095b8c3312a39326d1473e","user":{"login":"chef","id":29740,"avatar_url":"https://avatars3.githubusercontent.com/u/29740?v=4","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"repo":{"id":44141707,"name":"train","full_name":"chef/train","owner":{"login":"chef","id":29740,"avatar_url":"https://avatars3.githubusercontent.com/u/29740?v=4","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/chef/train","description":"Transport Interface to unify communication over SSH, WinRM, and friends.","fork":false,"url":"https://api.github.com/repos/chef/train","forks_url":"https://api.github.com/repos/chef/train/forks","keys_url":"https://api.github.com/repos/chef/train/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chef/train/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chef/train/teams","hooks_url":"https://api.github.com/repos/chef/train/hooks","issue_events_url":"https://api.github.com/repos/chef/train/issues/events{/number}","events_url":"https://api.github.com/repos/chef/train/events","assignees_url":"https://api.github.com/repos/chef/train/assignees{/user}","branches_url":"https://api.github.com/repos/chef/train/branches{/branch}","tags_url":"https://api.github.com/repos/chef/train/tags","blobs_url":"https://api.github.com/repos/chef/train/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chef/train/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chef/train/git/refs{/sha}","trees_url":"https://api.github.com/repos/chef/train/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chef/train/statuses/{sha}","languages_url":"https://api.github.com/repos/chef/train/languages","stargazers_url":"https://api.github.com/repos/chef/train/stargazers","contributors_url":"https://api.github.com/repos/chef/train/contributors","subscribers_url":"https://api.github.com/repos/chef/train/subscribers","subscription_url":"https://api.github.com/repos/chef/train/subscription","commits_url":"https://api.github.com/repos/chef/train/commits{/sha}","git_commits_url":"https://api.github.com/repos/chef/train/git/commits{/sha}","comments_url":"https://api.github.com/repos/chef/train/comments{/number}","issue_comment_url":"https://api.github.com/repos/chef/train/issues/comments{/number}","contents_url":"https://api.github.com/repos/chef/train/contents/{+path}","compare_url":"https://api.github.com/repos/chef/train/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chef/train/merges","archive_url":"https://api.github.com/repos/chef/train/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chef/train/downloads","issues_url":"https://api.github.com/repos/chef/train/issues{/number}","pulls_url":"https://api.github.com/repos/chef/train/pulls{/number}","milestones_url":"https://api.github.com/repos/chef/train/milestones{/number}","notifications_url":"https://api.github.com/repos/chef/train/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chef/train/labels{/name}","releases_url":"https://api.github.com/repos/chef/train/releases{/id}","deployments_url":"https://api.github.com/repos/chef/train/deployments","created_at":"2015-10-13T00:26:45Z","updated_at":"2018-01-31T19:51:12Z","pushed_at":"2018-02-02T17:22:29Z","git_url":"git://github.com/chef/train.git","ssh_url":"git@github.com:chef/train.git","clone_url":"https://github.com/chef/train.git","svn_url":"https://github.com/chef/train","homepage":null,"size":631,"stargazers_count":53,"watchers_count":53,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":41,"mirror_url":null,"archived":false,"open_issues_count":35,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":41,"open_issues":35,"watchers":53,"default_branch":"master"}},"base":{"label":"chef:master","ref":"master","sha":"d7b958c4cd1ee3b7993bd0fc05a85fb25544046c","user":{"login":"chef","id":29740,"avatar_url":"https://avatars3.githubusercontent.com/u/29740?v=4","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"repo":{"id":44141707,"name":"train","full_name":"chef/train","owner":{"login":"chef","id":29740,"avatar_url":"https://avatars3.githubusercontent.com/u/29740?v=4","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/chef/train","description":"Transport Interface to unify communication over SSH, WinRM, and friends.","fork":false,"url":"https://api.github.com/repos/chef/train","forks_url":"https://api.github.com/repos/chef/train/forks","keys_url":"https://api.github.com/repos/chef/train/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chef/train/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chef/train/teams","hooks_url":"https://api.github.com/repos/chef/train/hooks","issue_events_url":"https://api.github.com/repos/chef/train/issues/events{/number}","events_url":"https://api.github.com/repos/chef/train/events","assignees_url":"https://api.github.com/repos/chef/train/assignees{/user}","branches_url":"https://api.github.com/repos/chef/train/branches{/branch}","tags_url":"https://api.github.com/repos/chef/train/tags","blobs_url":"https://api.github.com/repos/chef/train/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chef/train/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chef/train/git/refs{/sha}","trees_url":"https://api.github.com/repos/chef/train/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chef/train/statuses/{sha}","languages_url":"https://api.github.com/repos/chef/train/languages","stargazers_url":"https://api.github.com/repos/chef/train/stargazers","contributors_url":"https://api.github.com/repos/chef/train/contributors","subscribers_url":"https://api.github.com/repos/chef/train/subscribers","subscription_url":"https://api.github.com/repos/chef/train/subscription","commits_url":"https://api.github.com/repos/chef/train/commits{/sha}","git_commits_url":"https://api.github.com/repos/chef/train/git/commits{/sha}","comments_url":"https://api.github.com/repos/chef/train/comments{/number}","issue_comment_url":"https://api.github.com/repos/chef/train/issues/comments{/number}","contents_url":"https://api.github.com/repos/chef/train/contents/{+path}","compare_url":"https://api.github.com/repos/chef/train/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chef/train/merges","archive_url":"https://api.github.com/repos/chef/train/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chef/train/downloads","issues_url":"https://api.github.com/repos/chef/train/issues{/number}","pulls_url":"https://api.github.com/repos/chef/train/pulls{/number}","milestones_url":"https://api.github.com/repos/chef/train/milestones{/number}","notifications_url":"https://api.github.com/repos/chef/train/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chef/train/labels{/name}","releases_url":"https://api.github.com/repos/chef/train/releases{/id}","deployments_url":"https://api.github.com/repos/chef/train/deployments","created_at":"2015-10-13T00:26:45Z","updated_at":"2018-01-31T19:51:12Z","pushed_at":"2018-02-02T17:22:29Z","git_url":"git://github.com/chef/train.git","ssh_url":"git@github.com:chef/train.git","clone_url":"https://github.com/chef/train.git","svn_url":"https://github.com/chef/train","homepage":null,"size":631,"stargazers_count":53,"watchers_count":53,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":41,"mirror_url":null,"archived":false,"open_issues_count":35,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":41,"open_issues":35,"watchers":53,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/chef/train/pulls/250"},"html":{"href":"https://github.com/chef/train/pull/250"},"issue":{"href":"https://api.github.com/repos/chef/train/issues/250"},"comments":{"href":"https://api.github.com/repos/chef/train/issues/250/comments"},"review_comments":{"href":"https://api.github.com/repos/chef/train/pulls/250/comments"},"review_comment":{"href":"https://api.github.com/repos/chef/train/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/chef/train/pulls/250/commits"},"statuses":{"href":"https://api.github.com/repos/chef/train/statuses/4403003254a96d5db5095b8c3312a39326d1473e"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"adamleff"}}