kitchen-azurerm 1.13.6 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kitchen/driver/azure_credentials.rb +153 -82
- data/lib/kitchen/driver/azurerm.rb +574 -380
- data/lib/kitchen/driver/azurerm_version.rb +7 -1
- data/templates/internal.erb +15 -13
- data/templates/public.erb +15 -13
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c8f93443204a7cb375624116dbfafaa5ade3419fdd9e0538e1c701f38e60d29
|
|
4
|
+
data.tar.gz: e14d346d4cb82139aaaa49fdb9d4ef7f752059e0b535d24285e37a585c5b710b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2474a4e576e924afaa92e121fc315fee4cbce2ab159f946d64d8e208f1127d8dd50fd7d383aafc70beec2bb3dfb9194ddaf79d43c5e4cd1522787a4d38603efa
|
|
7
|
+
data.tar.gz: 9e498c0a069a4ee74e7db388c5983df082ba1bf251be06280868abdc802de616f75a93e331b62527a39b839cf55927fcab741265d732eef64f460e34130099e5
|
|
@@ -1,42 +1,96 @@
|
|
|
1
1
|
require "inifile"
|
|
2
2
|
|
|
3
|
+
require "kitchen/errors"
|
|
3
4
|
require "kitchen/logging"
|
|
4
5
|
autoload :MsRest2, "ms_rest2"
|
|
5
6
|
autoload :MsRestAzure2, "ms_rest_azure2"
|
|
6
7
|
|
|
7
8
|
module Kitchen
|
|
8
9
|
module Driver
|
|
10
|
+
# Resolves Azure Resource Manager credentials and endpoint settings for a
|
|
11
|
+
# single subscription.
|
|
9
12
|
#
|
|
10
|
-
#
|
|
13
|
+
# Credentials are sourced, in order of precedence, from environment
|
|
14
|
+
# variables (+AZURE_TENANT_ID+, +AZURE_CLIENT_ID+, +AZURE_CLIENT_SECRET+)
|
|
15
|
+
# and then from the Azure CLI credentials INI file (by default
|
|
16
|
+
# +~/.azure/credentials+, overridable with +AZURE_CONFIG_FILE+).
|
|
11
17
|
#
|
|
18
|
+
# The combination of values that resolve determines which token provider is
|
|
19
|
+
# used - see {#azure_options}.
|
|
20
|
+
#
|
|
21
|
+
# @example Service principal supplied by environment
|
|
22
|
+
# ENV["AZURE_TENANT_ID"] = "..."
|
|
23
|
+
# ENV["AZURE_CLIENT_ID"] = "..."
|
|
24
|
+
# ENV["AZURE_CLIENT_SECRET"] = "..."
|
|
25
|
+
# options = Kitchen::Driver::AzureCredentials.new(subscription_id: "...").azure_options
|
|
12
26
|
class AzureCredentials
|
|
13
27
|
include Kitchen::Logging
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
# Path fragment, relative to the user's home directory, of the Azure CLI
|
|
30
|
+
# credentials file.
|
|
17
31
|
#
|
|
18
32
|
# @return [String]
|
|
33
|
+
CONFIG_FILE = File.join(".azure", "credentials").freeze
|
|
34
|
+
|
|
35
|
+
# Azure cloud names understood by {#azure_options}, mapped to the
|
|
36
|
+
# +MsRestAzure2+ constants that describe them. Keys are downcased so
|
|
37
|
+
# lookups are case-insensitive.
|
|
19
38
|
#
|
|
20
|
-
|
|
39
|
+
# @return [Hash{String => Symbol}]
|
|
40
|
+
ENVIRONMENTS = {
|
|
41
|
+
"azure" => :Azure,
|
|
42
|
+
"azurechina" => :AzureChina,
|
|
43
|
+
"azuregermancloud" => :AzureGermanCloud,
|
|
44
|
+
"azureusgovernment" => :AzureUSGovernment,
|
|
45
|
+
}.freeze
|
|
21
46
|
|
|
47
|
+
# Port the Azure Instance Metadata Service listens on for MSI token requests.
|
|
48
|
+
#
|
|
49
|
+
# @return [Integer]
|
|
50
|
+
MSI_PORT = 50342
|
|
51
|
+
|
|
52
|
+
# The Azure subscription these credentials authenticate against.
|
|
22
53
|
#
|
|
23
54
|
# @return [String]
|
|
55
|
+
attr_reader :subscription_id
|
|
56
|
+
|
|
57
|
+
# The Azure cloud name, e.g. +"Azure"+ or +"AzureUSGovernment"+.
|
|
24
58
|
#
|
|
59
|
+
# @return [String]
|
|
25
60
|
attr_reader :environment
|
|
26
61
|
|
|
62
|
+
# Default path of the Azure CLI credentials file.
|
|
27
63
|
#
|
|
28
|
-
#
|
|
64
|
+
# Resolved lazily (rather than at load time) so that a test - or a caller
|
|
65
|
+
# that manipulates +HOME+ - sees the current home directory rather than
|
|
66
|
+
# whichever one happened to be set when this file was first required.
|
|
29
67
|
#
|
|
68
|
+
# @return [String] absolute path to +~/.azure/credentials+
|
|
69
|
+
def self.default_config_path
|
|
70
|
+
File.join(Dir.home, CONFIG_FILE)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @param subscription_id [String] the Azure subscription to authenticate against.
|
|
74
|
+
# @param environment [String] the Azure cloud name. Case-insensitive.
|
|
75
|
+
# @raise [Kitchen::UserError] if +environment+ is not a known Azure cloud.
|
|
30
76
|
def initialize(subscription_id:, environment: "Azure")
|
|
31
77
|
@subscription_id = subscription_id
|
|
32
|
-
@environment = environment
|
|
78
|
+
@environment = environment || "Azure"
|
|
79
|
+
|
|
80
|
+
unless ENVIRONMENTS.key?(@environment.to_s.downcase)
|
|
81
|
+
raise Kitchen::UserError,
|
|
82
|
+
"Unknown azure_environment '#{@environment}'. Valid values are: #{ENVIRONMENTS.keys.join(", ")} (case-insensitive)."
|
|
83
|
+
end
|
|
33
84
|
end
|
|
34
85
|
|
|
86
|
+
# Builds the options hash accepted by every +azure_mgmt_*2+ client.
|
|
35
87
|
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
88
|
+
# The +:credentials+ entry wraps whichever token provider matches the
|
|
89
|
+
# resolved credentials - see {#token_provider}. +:client_id+ and
|
|
90
|
+
# +:client_secret+ are only included when they resolve to a value.
|
|
39
91
|
#
|
|
92
|
+
# @return [Hash] options suitable for
|
|
93
|
+
# +Azure::Resources2::Profiles::Latest::Mgmt::Client.new+ and friends.
|
|
40
94
|
def azure_options
|
|
41
95
|
options = { tenant_id: tenant_id!,
|
|
42
96
|
subscription_id:,
|
|
@@ -48,16 +102,76 @@ module Kitchen
|
|
|
48
102
|
options
|
|
49
103
|
end
|
|
50
104
|
|
|
51
|
-
|
|
105
|
+
# Selects a token provider based on which credentials resolved.
|
|
106
|
+
#
|
|
107
|
+
# * +client_id+ + +client_secret+ + +tenant_id+ - service principal.
|
|
108
|
+
# * +client_id+ + +tenant_id+ - user-assigned managed identity.
|
|
109
|
+
# * +tenant_id+ only - system-assigned managed identity.
|
|
110
|
+
# * none of the above - falls back to the +az login+ token cache.
|
|
111
|
+
#
|
|
112
|
+
# @return [MsRestAzure2::ApplicationTokenProvider,
|
|
113
|
+
# MsRestAzure2::MSITokenProvider, MsRestAzure2::AzureCliTokenProvider]
|
|
114
|
+
def token_provider
|
|
115
|
+
if client_id && client_secret && tenant_id
|
|
116
|
+
::MsRestAzure2::ApplicationTokenProvider.new(tenant_id, client_id, client_secret, ad_settings)
|
|
117
|
+
elsif client_id && tenant_id
|
|
118
|
+
::MsRestAzure2::MSITokenProvider.new(MSI_PORT, ad_settings, { client_id: })
|
|
119
|
+
elsif tenant_id
|
|
120
|
+
::MsRestAzure2::MSITokenProvider.new(MSI_PORT, ad_settings)
|
|
121
|
+
else
|
|
122
|
+
warn("Using tenant id set through `az login`.")
|
|
123
|
+
::MsRestAzure2::AzureCliTokenProvider.new(ad_settings)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
52
126
|
|
|
53
|
-
|
|
54
|
-
|
|
127
|
+
# Active Directory settings for the configured cloud.
|
|
128
|
+
#
|
|
129
|
+
# @return [MsRestAzure2::ActiveDirectoryServiceSettings]
|
|
130
|
+
def ad_settings
|
|
131
|
+
case environment_key
|
|
132
|
+
when :AzureUSGovernment then ::MsRestAzure2::ActiveDirectoryServiceSettings.get_azure_us_government_settings
|
|
133
|
+
when :AzureChina then ::MsRestAzure2::ActiveDirectoryServiceSettings.get_azure_china_settings
|
|
134
|
+
when :AzureGermanCloud then ::MsRestAzure2::ActiveDirectoryServiceSettings.get_azure_german_settings
|
|
135
|
+
else ::MsRestAzure2::ActiveDirectoryServiceSettings.get_azure_settings
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Endpoint settings (resource manager URL, storage suffixes, ...) for the
|
|
140
|
+
# configured cloud.
|
|
141
|
+
#
|
|
142
|
+
# @return [MsRestAzure2::AzureEnvironment]
|
|
143
|
+
def endpoint_settings
|
|
144
|
+
case environment_key
|
|
145
|
+
when :AzureUSGovernment then ::MsRestAzure2::AzureEnvironments::AzureUSGovernment
|
|
146
|
+
when :AzureChina then ::MsRestAzure2::AzureEnvironments::AzureChinaCloud
|
|
147
|
+
when :AzureGermanCloud then ::MsRestAzure2::AzureEnvironments::AzureGermanCloud
|
|
148
|
+
else ::MsRestAzure2::AzureEnvironments::AzureCloud
|
|
149
|
+
end
|
|
55
150
|
end
|
|
56
151
|
|
|
152
|
+
# Path of the credentials file actually in use.
|
|
153
|
+
#
|
|
154
|
+
# @return [String] +AZURE_CONFIG_FILE+ if set, otherwise
|
|
155
|
+
# {.default_config_path}. Always expanded.
|
|
57
156
|
def config_path
|
|
58
|
-
@config_path ||= File.expand_path(ENV["AZURE_CONFIG_FILE"] ||
|
|
157
|
+
@config_path ||= File.expand_path(ENV["AZURE_CONFIG_FILE"] || self.class.default_config_path)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
# @return [Symbol] the {ENVIRONMENTS} key for the configured cloud.
|
|
163
|
+
def environment_key
|
|
164
|
+
ENVIRONMENTS.fetch(environment.to_s.downcase)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @return [Kitchen::Logger] the shared Test Kitchen logger.
|
|
168
|
+
def logger
|
|
169
|
+
Kitchen.logger
|
|
59
170
|
end
|
|
60
171
|
|
|
172
|
+
# Parsed credentials file, or an empty Hash when no readable file exists.
|
|
173
|
+
#
|
|
174
|
+
# @return [IniFile, Hash]
|
|
61
175
|
def credentials
|
|
62
176
|
@credentials ||= if File.file?(config_path)
|
|
63
177
|
IniFile.load(config_path)
|
|
@@ -67,94 +181,51 @@ module Kitchen
|
|
|
67
181
|
end
|
|
68
182
|
end
|
|
69
183
|
|
|
184
|
+
# Reads a property from the section of the credentials file matching
|
|
185
|
+
# {#subscription_id}.
|
|
186
|
+
#
|
|
187
|
+
# @param property [String] the INI key to read.
|
|
188
|
+
# @return [String, nil]
|
|
70
189
|
def credentials_property(property)
|
|
71
|
-
credentials[subscription_id]&.[](property)
|
|
190
|
+
value = credentials[subscription_id]&.[](property)
|
|
191
|
+
value unless value.to_s.empty?
|
|
72
192
|
end
|
|
73
193
|
|
|
194
|
+
# Tenant ID, warning the user when one cannot be resolved.
|
|
195
|
+
#
|
|
196
|
+
# @return [String, nil]
|
|
74
197
|
def tenant_id!
|
|
75
198
|
tenant_id || warn("(#{config_path}) does not contain tenant_id neither is the AZURE_TENANT_ID environment variable set.")
|
|
76
199
|
end
|
|
77
200
|
|
|
201
|
+
# @return [String, nil] tenant ID from the environment or credentials file.
|
|
78
202
|
def tenant_id
|
|
79
|
-
|
|
203
|
+
env_or_credentials("AZURE_TENANT_ID", "tenant_id")
|
|
80
204
|
end
|
|
81
205
|
|
|
206
|
+
# @return [String, nil] client ID from the environment or credentials file.
|
|
82
207
|
def client_id
|
|
83
|
-
|
|
208
|
+
env_or_credentials("AZURE_CLIENT_ID", "client_id")
|
|
84
209
|
end
|
|
85
210
|
|
|
211
|
+
# @return [String, nil] client secret from the environment or credentials file.
|
|
86
212
|
def client_secret
|
|
87
|
-
|
|
213
|
+
env_or_credentials("AZURE_CLIENT_SECRET", "client_secret")
|
|
88
214
|
end
|
|
89
215
|
|
|
90
|
-
#
|
|
216
|
+
# Reads a value from the environment, falling back to the credentials file.
|
|
91
217
|
#
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
# Login with a credentials file or setting the environment variables
|
|
95
|
-
#
|
|
96
|
-
# Typically used with a service principal.
|
|
97
|
-
#
|
|
98
|
-
# SPN with client_id, client_secret and tenant_id
|
|
99
|
-
if client_id && client_secret && tenant_id
|
|
100
|
-
::MsRestAzure2::ApplicationTokenProvider.new(tenant_id, client_id, client_secret, ad_settings)
|
|
101
|
-
# Login with a Managed Service Identity.
|
|
102
|
-
#
|
|
103
|
-
# Typically used with a Managed Service Identity when you have a particular object registered in a tenant.
|
|
104
|
-
#
|
|
105
|
-
# MSI with client_id and tenant_id (aka User Assigned Identity).
|
|
106
|
-
elsif client_id && tenant_id
|
|
107
|
-
::MsRestAzure2::MSITokenProvider.new(50342, ad_settings, { client_id: })
|
|
108
|
-
# Default approach to inheriting existing object permissions (application or device this code is running on).
|
|
109
|
-
#
|
|
110
|
-
# Typically used when you want to inherit the permissions of the system you're running on that are in a tenant.
|
|
111
|
-
#
|
|
112
|
-
# MSI with just tenant_id (aka System Assigned Identity).
|
|
113
|
-
elsif tenant_id
|
|
114
|
-
::MsRestAzure2::MSITokenProvider.new(50342, ad_settings)
|
|
115
|
-
# Login using the Azure CLI
|
|
116
|
-
#
|
|
117
|
-
# Typically used when you want to rely upon `az login` as your preferred authentication method.
|
|
118
|
-
else
|
|
119
|
-
warn("Using tenant id set through `az login`.")
|
|
120
|
-
::MsRestAzure2::AzureCliTokenProvider.new(ad_settings)
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
#
|
|
125
|
-
# Retrieves a [MsRestAzure2::ActiveDirectoryServiceSettings] object representing the AD settings for the given cloud.
|
|
126
|
-
#
|
|
127
|
-
# @return [MsRestAzure2::ActiveDirectoryServiceSettings] Settings to be used for subsequent requests
|
|
218
|
+
# Empty environment variables are treated as unset - an exported-but-blank
|
|
219
|
+
# +AZURE_CLIENT_SECRET+ should not shadow a real value in the file.
|
|
128
220
|
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
when "azuregermancloud"
|
|
136
|
-
::MsRestAzure2::ActiveDirectoryServiceSettings.get_azure_german_settings
|
|
137
|
-
when "azure"
|
|
138
|
-
::MsRestAzure2::ActiveDirectoryServiceSettings.get_azure_settings
|
|
139
|
-
end
|
|
140
|
-
end
|
|
221
|
+
# @param env_var [String] environment variable name.
|
|
222
|
+
# @param property [String] INI property name.
|
|
223
|
+
# @return [String, nil]
|
|
224
|
+
def env_or_credentials(env_var, property)
|
|
225
|
+
value = ENV[env_var]
|
|
226
|
+
return value unless value.to_s.empty?
|
|
141
227
|
|
|
142
|
-
|
|
143
|
-
# Retrieves a [MsRestAzure2::AzureEnvironment] object representing endpoint settings for the given cloud.
|
|
144
|
-
#
|
|
145
|
-
# @return [MsRestAzure2::AzureEnvironment] Settings to be used for subsequent requests
|
|
146
|
-
#
|
|
147
|
-
def endpoint_settings
|
|
148
|
-
case environment.downcase
|
|
149
|
-
when "azureusgovernment"
|
|
150
|
-
::MsRestAzure2::AzureEnvironments::AzureUSGovernment
|
|
151
|
-
when "azurechina"
|
|
152
|
-
::MsRestAzure2::AzureEnvironments::AzureChinaCloud
|
|
153
|
-
when "azuregermancloud"
|
|
154
|
-
::MsRestAzure2::AzureEnvironments::AzureGermanCloud
|
|
155
|
-
when "azure"
|
|
156
|
-
::MsRestAzure2::AzureEnvironments::AzureCloud
|
|
157
|
-
end
|
|
228
|
+
credentials_property(property)
|
|
158
229
|
end
|
|
159
230
|
end
|
|
160
231
|
end
|