knife-azure 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # Knife Azure
2
+
3
+ ## Description
4
+ A [knife] (http://docs.opscode.com/knife.html) plugin to create,
5
+ delete, and enumerate
6
+ [Windows Azure] (https://www.windowsazure.com)
7
+ resources to be managed by Chef.
8
+
9
+ ## Installation
10
+ Be sure you are running the latest version of Chef, which can be installed
11
+ via:
12
+
13
+ gem install chef
14
+
15
+ This plugin is distributed as a Ruby Gem. To install it, run:
16
+
17
+ gem install knife-azure
18
+
19
+ Depending on your system's configuration, you may need to run this command
20
+ with root/administrator privileges.
21
+
22
+ ## Configuration
23
+ For this plugin to interact with Azure's REST API, you will need to give Knife
24
+ information about your Azure account and credentials. The easiest way to do
25
+ this is to sign in to the Azure portal and download a publishsettings file
26
+ from https://manage.windowsazure.com/publishsettings/index?client=xplat to a
27
+ local file system location, and
28
+ then refer to the local file via an entry in your knife.rb:
29
+
30
+ knife[:azure_publish_settings_file] = "~/myazure.publishsettings"
31
+
32
+ Alternatively, all subcommands for this plugin will accept an
33
+ --azure-publish-settings-file option to allow you to specify the path to that
34
+ file with each command invocation.
35
+
36
+ The plug-in also accepts authentication information specified using an
37
+ alternative set of options -- see the section on "Alternative Management
38
+ Certificate Specification" for details.
39
+
40
+ ## Basic Examples
41
+ The following examples assume that you've configured the publishsettings file
42
+ location in your knife.rb:
43
+
44
+ # List images for use in creating new VM's:
45
+ $ knife azure image list
46
+
47
+ # List all VM's (including those not be managed by Chef)
48
+ $ knife azure server list
49
+
50
+ # Create and bootstrap an Ubuntu VM over ssh
51
+ $ knife azure server create -N MyNewNode --azure-vm-size Medium --I 8fcc3d_Ubuntu-12_04-amd64-30GB -m 'West US' --ssh-user myuser --identity-file ~/.ssh/myprivatekey_rsa
52
+
53
+ # Create and bootstrap a Windows VM over winrm
54
+ $ knife azure server create --azure-dns-name MyNewServerName --azure-vm-size Medium --I 8fcc3d_Win2012-amd64-30GB -m 'West US' --winrm-user myuser --winrm-password 'mypassword' --bootstrap-protocol winrm --distro 'windows-chef-client-msi'
55
+
56
+ # Delete a server and purge it from the Chef server
57
+ $ knife azure server delete MyNewNode --purge -y
58
+
59
+ Use the --help option to read more about each subcommand. Eg:
60
+
61
+ knife azure server create --help
62
+
63
+ ## Detailed Usage
64
+
65
+ ### Common Configuration
66
+ Most configuration options can be specified either in your knife.rb file or as command line parameters. The CLI parameters override the knife.rb parameters.
67
+
68
+ The following options are required for all subcommands:
69
+
70
+ option :azure_publish_settings_file Path to your .publishsettings file
71
+
72
+ OR
73
+
74
+ option :azure_subscription_id Your Azure subscription ID
75
+ option :azure_mgmt_cert Management certificate in PEM format
76
+ option :azure_api_host_name Your Azure API host name
77
+
78
+ ### Azure Image List Subcommand
79
+ Outputs a list of all linux images that are available to use for provisioning. You should choose one of these to use for the :azure_source_image parameter to the server create command. You can use the filter option to see a detailed image list.
80
+
81
+ knife azure image list
82
+
83
+ ### Azure Server Create Subcommand
84
+ This subcommand provisions a new server in Azure and then performs a Chef bootstrap. The goal of the bootstrap is to get Chef installed on the target system so it can run Chef Client with a Chef Server.
85
+
86
+ #### Windows Bootstrapping Requirements
87
+ knife-azure depends on knife-windows: https://github.com/opscode/knife-windows
88
+ to bootstrap Windows machines via winrm (Basic, NTLM and Kerberos authentication) or ssh.
89
+
90
+ The distro/template to be used for bootstrapping is: https://github.com/opscode/knife-windows/blob/master/lib/chef/knife/bootstrap/windows-chef-client-msi.erb
91
+
92
+ Windows source images should have the WinRM service enabled and the
93
+ authentication should be set accordingly (Basic, NTLM and Kerberos). Firewall rules should be added accordingly to the source images. Refer to the link to configure this:
94
+ https://github.com/opscode/knife-windows#nodes
95
+
96
+ #### Azure-specific Options
97
+ :azure_dns_name Required. The DNS prefix name that can be used to access the cloud
98
+ service which is unique within Windows Azure. If you want to add
99
+ new VM to an existing service/deployment, specify an exiting
100
+ dns-name, along with --azure-connect-to-existing-dns option. Otherwise
101
+ a new deployment is created.
102
+ :azure_service_location Required. Specifies the geographic location of the resource as the
103
+ name of a datacenter location that is valid for your subscription. Eg:
104
+ West US, East US, East Asia, Southeast Asia, North Europe, West Europe.
105
+ :azure_source_image Required. Specifies the name of the disk image to use to create
106
+ the virtual machine. Do a "knife azure image list" to see a
107
+ list of available images.
108
+ :azure_storage_account A name for the storage account that is unique within Windows Azure.
109
+ Storage account names must be between 3 and 24 characters in
110
+ length and use numbers and lower-case letters only. This name is
111
+ the DNS prefix name and can be used to access blobs, queues, and
112
+ tables in the storage account.
113
+ :azure_vm_name Specifies the name for the virtual machine. The name must
114
+ be unique within the deployment.
115
+ :azure_os_disk_name Optional. Specifies the friendly name of the disk containing
116
+ the guest OS image in the image repository.
117
+ :azure_vm_size Size of virtual machine. Default is Small.
118
+ (ExtraSmall, Small, Medium, Large, ExtraLarge)
119
+ :azure_connect_to_existing_dns Set this flag to add the new VM to an existing
120
+ deployment/service. Must give the name of the existing
121
+ DNS correctly in the --azure-dns-name option
122
+
123
+ #### Azure VM Quick Create
124
+ You can create a server with minimal configuration. On the Azure Management Portal, this corresponds to a "Quick Create - VM". Sample command for quick create (for an Ubuntu instance):
125
+
126
+ knife azure server create
127
+ --azure-publish-settings-file '/path/to/your/cert.publishsettingsfile'
128
+ --azure-dns-name 'myservice'
129
+ --azure-service-location 'West US'
130
+ --azure-source-image 'source-image-name'
131
+ --ssh-user 'jetstream'
132
+ --identity-file '~/.ssh/myazure_rsa'
133
+
134
+ Note that the --identity-file option, which enables specification of a private
135
+ key authorized to communicate securely with the created server during the
136
+ bootstrap process, will also configure the user specified by --ssh-user with
137
+ the public key that corresponds to the private key specified by
138
+ --identity-file. This configuration persists even after the create subcommand
139
+ has completed successfully, so that the key specified with --identity-file can
140
+ be used with ssh clients for subsequent access to the server as the user
141
+ specified by --ssh-user.
142
+
143
+ You can set these options from knife.rb. A typical knife.rb is
144
+ shown below:
145
+
146
+ knife[:azure_publish_settings_file] = '/path/to/your/cert.publishsettingsfile'
147
+ knife[:azure_dns_name] = 'myservice'
148
+ knife[:azure_service_location] = 'West US'
149
+ knife[:azure_source_image] = 'source-image-name'
150
+ knife[:ssh_user] = 'jetstream'
151
+ knife[:identity_file] = '~/.ssh/myazure_rsa'
152
+
153
+ #### Azure VM Advanced Create
154
+ You can set various other options in the advanced create.
155
+ Eg: If you want to set the Azure VM Name different from that of the Azure DNS Name, set the option :azure_vm_name.
156
+ Eg: If you want to specify a Storage Account Name, set the option :azure_storage_account
157
+
158
+ To connect to an existing DNS/service, you can use a command as below:
159
+
160
+ knife azure server create
161
+ --azure-subscription-id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
162
+ --azure-mgmt-cert '/path/to/your/mgmtCert.pem'
163
+ --azure-api-host-name 'management.core.windows.net'
164
+ --azure-connect-to-existing-dns
165
+ --azure-dns-name 'myservice'
166
+ --azure-vm-name 'myvm02'
167
+ --azure-service-location 'West US'
168
+ --azure-source-image 'source-image-name'
169
+ --ssh-user 'jetstream'
170
+ --ssh-password 'jetstream@123'
171
+
172
+ These options may also be configured from knife.rb, as in this example:
173
+
174
+ knife[:azure_subscription_id] = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
175
+ knife[:azure_mgmt_cert] = '/path/to/your/mgmtCert.pem'
176
+ knife[:azure_api_host_name] = 'management.core.windows.net'
177
+ knife[:azure_service_location] = 'West US'
178
+ knife[:azure_dns_name]='myservice'
179
+ knife[:azure_vm_name]='myvm02'
180
+ knife[:ssh_user]='jetstream'
181
+ knife[:identity_file]='/path/to/RSA/private/key'
182
+ knife[:azure_storage_account]='auxpreview104'
183
+ knife[:azure_os_disk_name]='disk107'
184
+ knife[:tcp_endpoints]='66'
185
+ knife[:udp_endpoints]='77,88,99'
186
+
187
+ #### Options for Bootstrapping a Windows Node in Azure
188
+
189
+ :bootstrap_protocol Default is winrm for a windows image
190
+ :winrm_password The WinRM password
191
+ :winrm_port The WinRM port, by default this is 5985
192
+ :winrm_transport The WinRM transport type. valid choices are [ssl, plaintext]
193
+ :kerberos_keytab_file The Kerberos keytab file used for authentication
194
+ :kerberos_realm The Kerberos realm used for authentication
195
+ :kerberos_service The Kerberos service used for authentication
196
+ :ca_trust_file The Certificate Authority (CA) trust file used for SSL transport
197
+
198
+ #### Azure Windows Node Create
199
+ The quick create option requires the following options for a windows instance:
200
+
201
+ knife azure server create
202
+ --azure-publish-settings-file '/path/to/your/cert.publishsettingsfile'
203
+ --azure-dns-name 'myserverdnsname'
204
+ --azure-service-location 'West US'
205
+ --azure-source-image 'windows-2012-image-id'
206
+ --winrm-user 'jetstream'
207
+ --winrm-password 'jetstream@123'
208
+ --distro 'windows-chef-client-msi'
209
+
210
+ Sample knife.rb for bootstrapping Windows Node with basic authentication
211
+
212
+ knife[:bootstrap_protocol] = 'winrm'
213
+ knife[:winrm_password] = 'mgcvTuvV2Rh'
214
+ knife[:winrm_user] = 'myuser'
215
+ knife[:winrm_port] = '5985'
216
+ knife[:distro] = 'windows-chef-client-msi'
217
+ knife[:azure_source_image]='windows-2012-image-id'
218
+
219
+ ### Azure Server Delete Subcommand
220
+ Deletes an existing server in the currently configured Azure account. By
221
+ default, this does not delete the associated node and client objects from the
222
+ Chef server. To do so, add the --purge flag. Also by default, the DNS name, also called "cloud service", is deleted if you are deleting the last VM from that service. By default, the OS disk is also deleted. If you want to retain them add the --preserve flag as shown below. To delete the storage account, add the --delete-azure-storage-account flag since by default the storage account is not deleted.
223
+
224
+ knife azure server delete "myvm01"
225
+ knife azure server delete "myvm01" --purge #purge chef node
226
+ knife azure server delete "myvm01" --preserve-azure-os-disk
227
+ knife azure server delete "myvm01" --preserve-azure-dns-name
228
+ knife azure server delete "myvm01" --delete-azure-storage-account
229
+
230
+ Since the VM name can be the same across DNS name, you must specify the DNS
231
+ name also to delete the VM. Sample command to delete a VM for a given DNS name:
232
+
233
+ knife azure server delete "myvm01" --azure-dns-name "mydnsname"
234
+ knife azure server delete "myvm01" "myvm02" --azure-dns-name "mydnsname"
235
+
236
+ ### Azure Server List Subcommand
237
+ Outputs a list of all servers in the currently configured Azure account. PLEASE NOTE - this shows all instances associated with the account, some of which may not be currently managed by the Chef server.
238
+
239
+ knife azure server list
240
+
241
+ ## Alternative Management Certificate Specification
242
+ In addition to specifying the management certificate using the publishsettings
243
+ file, you can also specify it in PEM format. Follow these steps to generate the certificate in the PEM format:
244
+
245
+ 1. Download the settings file from https://manage.windowsazure.com/publishsettings/index?client=xplat
246
+ 2. Extract the data from the ManagementCertificate field into a separate file named - cert.pfx
247
+ 3. Decode the certificate file:
248
+
249
+ #### On Linux/Mac(Homebrew)
250
+
251
+ base64 -d cert.pfx > cert_decoded.pfx
252
+
253
+ #### On Windows
254
+ You can decode and extract the PFX file using powershell or a free windows base 64 decoder such as http://www.fourmilab.ch/webtools/base64/base64.zip,
255
+
256
+ base64.exe -d cert.pfx -> cert_decoded.pfx
257
+
258
+ 4. Convert the decoded PFX file to a PEM file
259
+
260
+ #### On Linux/Mac(Homebrew)
261
+
262
+ openssl pkcs12 -in cert_decoded.pfx -out managementCertificate.pem -nodes
263
+
264
+ #### On Windows
265
+ Use powershell & run following command. If openssl.exe is not already installed it can be downloaded from http://www.openssl.org/related/binaries.html (Note: openssl depends on Microsoft Visual C++ Redistributable package (x86) which must be installed for openssl to function properly).
266
+
267
+ openssl base64 -d -A -in cert_decoded.pfx -out cert_decode.der
268
+
269
+ openssl pkcs12 -in cert_decoded.der -out managementCertificate.pem -nodes
270
+
271
+ You might be asked to enter a password which is usually blank.
272
+ You might be also asked to enter a passphrase. Please enter the phrase of your choice.
273
+
274
+ It is possible to generate your own certificates and upload them. More Detailed Documentation about the Management Certificates is available : https://www.windowsazure.com/en-us/manage/linux/common-tasks/manage-certificates/
275
+
@@ -0,0 +1,87 @@
1
+ #
2
+ # Author:: Mukta Aphale (mukta.aphale@clogeny.com)
3
+ # Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ class Azure
20
+ class Certificates
21
+ def initialize(connection)
22
+ @connection=connection
23
+ end
24
+ def create(params)
25
+ certificate = Certificate.new(@connection)
26
+ certificate.create(params)
27
+ end
28
+ end
29
+ end
30
+
31
+ class Azure
32
+ class Certificate
33
+ attr_accessor :connection
34
+ attr_accessor :cert_data, :fingerprint, :certificate_version
35
+ def initialize(connection)
36
+ @connection = connection
37
+ @certificate_version = 2 # cf. RFC 5280 - to make it a "v3" certificate
38
+ end
39
+ def create(params)
40
+ # If RSA private key has been specified, then generate an x 509 certificate from the
41
+ # public part of the key
42
+ @cert_data = generate_public_key_certificate_data({:ssh_key => params[:identity_file],
43
+ :ssh_key_passphrase => params[:identity_file_passphrase]})
44
+ # Generate XML to call the API
45
+ # Add certificate to the hosted service
46
+ builder = Nokogiri::XML::Builder.new do |xml|
47
+ xml.CertificateFile('xmlns'=>'http://schemas.microsoft.com/windowsazure') {
48
+ xml.Data @cert_data
49
+ xml.CertificateFormat 'pfx'
50
+ xml.Password 'knifeazure'
51
+ }
52
+ end
53
+ # Windows Azure API call
54
+ @connection.query_azure("hostedservices/#{params[:azure_dns_name]}/certificates", "post", builder.to_xml)
55
+ # Return the fingerprint to be used while adding role
56
+ @fingerprint
57
+ end
58
+
59
+ def generate_public_key_certificate_data (params)
60
+ # Generate OpenSSL RSA key from the mentioned ssh key path (and passphrase)
61
+ key = OpenSSL::PKey::RSA.new(File.read(params[:ssh_key]), params[:ssh_key_passphrase])
62
+ # Generate X 509 certificate
63
+ ca = OpenSSL::X509::Certificate.new
64
+ ca.version = @certificate_version
65
+ ca.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
66
+ ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=knife-plugin/CN=Opscode CA"
67
+ ca.issuer = ca.subject # root CA's are "self-signed"
68
+ ca.public_key = key.public_key # Assign the ssh-key's public part to the certificate
69
+ ca.not_before = Time.now
70
+ ca.not_after = ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
71
+ ef = OpenSSL::X509::ExtensionFactory.new
72
+ ef.subject_certificate = ca
73
+ ef.issuer_certificate = ca
74
+ ca.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
75
+ ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
76
+ ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
77
+ ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
78
+ ca.sign(key, OpenSSL::Digest::SHA256.new)
79
+ # Generate the SHA1 fingerprint of the der format of the X 509 certificate
80
+ @fingerprint = OpenSSL::Digest::SHA1.new(ca.to_der)
81
+ # Create the pfx format of the certificate
82
+ pfx = OpenSSL::PKCS12.create('knifeazure', 'knife-azure-pfx', key, ca)
83
+ # Encode the pfx format - upload this certificate
84
+ Base64.strict_encode64(pfx.to_der)
85
+ end
86
+ end
87
+ end
@@ -23,11 +23,12 @@ require File.expand_path('../deploy', __FILE__)
23
23
  require File.expand_path('../role', __FILE__)
24
24
  require File.expand_path('../disk', __FILE__)
25
25
  require File.expand_path('../image', __FILE__)
26
+ require File.expand_path('../certificate', __FILE__)
26
27
 
27
28
  class Azure
28
29
  class Connection
29
30
  include AzureAPI
30
- attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts
31
+ attr_accessor :hosts, :rest, :images, :deploys, :roles, :disks, :storageaccounts, :certificates
31
32
  def initialize(params={})
32
33
  @rest = Rest.new(params)
33
34
  @hosts = Hosts.new(self)
@@ -36,6 +37,7 @@ class Azure
36
37
  @deploys = Deploys.new(self)
37
38
  @roles = Roles.new(self)
38
39
  @disks = Disks.new(self)
40
+ @certificates = Certificates.new(self)
39
41
  end
40
42
  def query_azure(service_name, verb = 'get', body = '')
41
43
  Chef::Log.info 'calling ' + verb + ' ' + service_name
data/lib/azure/deploy.rb CHANGED
@@ -21,40 +21,69 @@ class Azure
21
21
  def initialize(connection)
22
22
  @connection=connection
23
23
  end
24
- def all
25
- deploys = Array.new
26
- hosts = @connection.hosts.all
27
- hosts.each do |host|
28
- deploy = Deploy.new(@connection)
29
- deploy.retrieve(host.name)
30
- unless deploy.name == nil
31
- deploys << deploy
24
+ # force_load should be true when there is something in local cache and we want to reload
25
+ # first call is always load.
26
+ def load(force_load = false)
27
+ if not @deploys || force_load
28
+ @deploys = begin
29
+ deploys = Array.new
30
+ hosts = @connection.hosts.all
31
+ hosts.each do |host|
32
+ deploy = Deploy.new(@connection)
33
+ deploy.retrieve(host.name)
34
+ if deploy.name
35
+ host.add_deploy(deploy)
36
+ deploys << deploy
37
+ end
38
+ end
39
+ deploys
32
40
  end
33
41
  end
34
- deploys
42
+ @deploys
43
+ end
44
+
45
+ def all
46
+ self.load
35
47
  end
36
- def find(hostedservicename)
37
- deployName = nil
38
- self.all.each do |deploy|
39
- next unless deploy.hostedservicename == hostedservicename
40
- deployName = deploy.name
48
+
49
+ # TODO - Current knife-azure plug-in seems to have assumption that single hostedservice
50
+ # will always have one deployment (production). see Deploy#retrieve below
51
+ def get_deploy_name_for_hostedservice(hostedservicename)
52
+ host = @connection.hosts.find(hostedservicename)
53
+ if host && host.deploys.length > 0
54
+ host.deploys[0].name
55
+ else
56
+ nil
41
57
  end
42
- deployName
43
58
  end
59
+
44
60
  def create(params)
45
- unless @connection.hosts.exists(params[:hosted_service_name])
46
- @connection.hosts.create(params)
47
- end
48
- unless @connection.storageaccounts.exists(params[:storage_account])
61
+ if params[:azure_connect_to_existing_dns]
62
+ unless @connection.hosts.exists?(params[:azure_dns_name])
63
+ Chef::Log.fatal 'The specified Azure DNS Name does not exist.'
64
+ exit 1
65
+ end
66
+ else
67
+ ret_val = @connection.hosts.create(params)
68
+ if ret_val.css('Error Code').length > 0
69
+ Chef::Log.fatal 'Unable to create DNS:' + ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content
70
+ exit 1
71
+ end
72
+ end
73
+ unless @connection.storageaccounts.exists?(params[:azure_storage_account])
49
74
  @connection.storageaccounts.create(params)
50
75
  end
51
- params['deploy_name'] = find(params[:hosted_service_name])
76
+ if params[:identity_file]
77
+ params[:fingerprint] = @connection.certificates.create(params)
78
+ end
79
+ params['deploy_name'] = get_deploy_name_for_hostedservice(params[:azure_dns_name])
80
+
52
81
  if params['deploy_name'] != nil
53
82
  role = Role.new(@connection)
54
83
  roleXML = role.setup(params)
55
84
  ret_val = role.create(params, roleXML)
56
85
  else
57
- params['deploy_name'] = params[:hosted_service_name]
86
+ params['deploy_name'] = params[:azure_dns_name]
58
87
  deploy = Deploy.new(@connection)
59
88
  deployXML = deploy.setup(params)
60
89
  ret_val = deploy.create(params, deployXML)
@@ -63,7 +92,7 @@ class Azure
63
92
  Chef::Log.fatal 'Unable to create role:' + ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content
64
93
  exit 1
65
94
  end
66
- @connection.roles.find(params[:role_name])
95
+ @connection.roles.find_in_hosted_service(params[:azure_vm_name], params[:azure_dns_name])
67
96
  end
68
97
  def delete(rolename)
69
98
  end
@@ -71,7 +100,8 @@ class Azure
71
100
 
72
101
  class Deploy
73
102
  include AzureUtility
74
- attr_accessor :connection, :name, :status, :url, :roles, :hostedservicename
103
+ attr_accessor :connection, :name, :status, :url, :hostedservicename
104
+
75
105
  def initialize(connection)
76
106
  @connection = connection
77
107
  end
@@ -82,12 +112,12 @@ class Azure
82
112
  @name = xml_content(deployXML, 'Deployment Name')
83
113
  @status = xml_content(deployXML,'Deployment Status')
84
114
  @url = xml_content(deployXML, 'Deployment Url')
85
- @roles = Array.new
115
+ @roles = Hash.new
86
116
  rolesXML = deployXML.css('Deployment RoleInstanceList RoleInstance')
87
117
  rolesXML.each do |roleXML|
88
118
  role = Role.new(@connection)
89
119
  role.parse(roleXML, hostedservicename, @name)
90
- @roles << role
120
+ @roles[role.name] = role
91
121
  end
92
122
  end
93
123
  end
@@ -98,7 +128,7 @@ class Azure
98
128
  builder = Nokogiri::XML::Builder.new do |xml|
99
129
  xml.Deployment(
100
130
  'xmlns'=>'http://schemas.microsoft.com/windowsazure',
101
- 'xmlns:i'=>'http://www.w3.org/2001/XMLSchema-instance'
131
+ 'xmlns:i'=>'http://www.w3.org/2001/XMLSchema-instance'
102
132
  ) {
103
133
  xml.Name params['deploy_name']
104
134
  xml.DeploymentSlot 'Production'
@@ -110,8 +140,22 @@ class Azure
110
140
  builder.doc
111
141
  end
112
142
  def create(params, deployXML)
113
- servicecall = "hostedservices/#{params[:hosted_service_name]}/deployments"
114
- @connection.query_azure(servicecall, "post", deployXML.to_xml)
143
+ servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments"
144
+ @connection.query_azure(servicecall, "post", deployXML.to_xml)
145
+ end
146
+
147
+ def roles
148
+ @roles.values if @roles
149
+ end
150
+
151
+ # just delete from local cache
152
+ def delete_role_if_present(role)
153
+ @roles.delete(role.name) if @roles
115
154
  end
155
+
156
+ def find_role(name)
157
+ @roles[name] if @roles
158
+ end
159
+
116
160
  end
117
161
  end