1
+ <#
2
+ # ----------------------------------------------------------------------------------
3
+ #
4
+ # Copyright Microsoft Corporation
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ----------------------------------------------------------------------------------
15
+
16
+ This script will allow to login as long as you have required certificate installed on your machine
17
+
18
+ WorkFlow# 1
19
+ ===========
20
+ 1) Will Check if the certificate is installed
21
+ 2) If not will ask you to login (one time) to access KeyVault
22
+ 3) Download Certificate, install certificate on the machine (will prompt to set certificate password)
23
+ 4) Login using the recently installed certificate
24
+ 5) Set the context for Test Subscription
25
+ 6) Download PublishSettings file from keyVault to enable log into ASM
26
+ 8) Run Tests
27
+ 9) Clean up - Delete any locally downloaded certificates or publishsettings file
28
+
29
+ WorkFlow# 2
30
+ ============
31
+ 1) Check if certificate is installed
32
+ 2) If Yes, log in using certificate
33
+ 3) Download publishsettings file from KeyVault
34
+ 4) Log into ASM using publish settings file
35
+ 5) Run Tests
36
+ 6) Clean up
37
+
38
+ TODO:
39
+ 1) Currently ServicePrincipal ClientId is hard-Coded in script
40
+ Will need to push that in KeyVault and use it to get the ClientId every single time you need to login using service principal
41
+ Will also need a way to replace existing ClientId ID, if Service principal is recreated.
42
+ <#--------Current certificate expires 10/7/2017 --------->
43
+ 2) Will need a way check if local certificate is near expiry, if yes create a new one in keyVault and delete existing one.
44
+ 3) When replacing Certificate, you will also need to set new role Assignment using the new certificate.
45
+
46
+ Issues:
47
+ 1) During the very first log in using certificate, an error pops up "Login-AzureRmAccount : No certificate was found in the certificate store with thumbprint System.Object[]"
48
+ This is a transient error, but eventually the login is successful as it finds the certificate in Currentuser store
49
+ So after the first try, this error no longer shows up.
50
+ Will open an issue to investigate where we try both the cert store simultenously rather we should try Currentuser and if not found then try LocalMachine.
51
+ #>
52
+
53
+ $global :gPsAutoTestADAppId = ' b8a1058e-25e8-4b08-b40b-d8d871dda591'
54
+ $global :gPsAutoTestSubscriptionName = ' Node CLI Test'
55
+ $global :gPsAutoTestSubscriptionId = ' 2c224e7e-3ef5-431d-a57b-e71f4662e3a6'
56
+
57
+ $global :gTenantId = ' 72f988bf-86f1-41af-91ab-2d7cd011db47'
58
+ $global :gLocalCertSubjectName = ' CN=PsAutoTestCert, OU=MicrosoftAzurePsTeam'
59
+ $global :gPfxLocalFileName = " PsAutoTestCert.pfx"
60
+ $global :gpubSettingLocalFileName = " NodeCLITest.publishsettings"
61
+
62
+ $global :gVaultName = ' KV-PsSdkAuto'
63
+ $global :gPsAutoResGrpName = ' AzurePsSdkAuto'
64
+
65
+ $global :gLocalCertStore = ' Cert:\CurrentUser\My'
66
+ $global :gCertPwd = ' '
67
+ $global :gLoggedInCtx = $null
68
+ $global :localPfxDirPath = [Environment ]::GetEnvironmentVariable(" TEMP" )
69
+
70
+ # KV KeyVaules
71
+ $global :kvSecKey_PsAutoTestCertNameKey = ' PsAutoTestCertName'
72
+ $global :gPsAutoTestADAppUsingSPNKey = ' PsAutoTestADAppUsingSPN'
73
+ $global :gkvSecKey_PubSettingFileNameKey = ' NodeCliTestPubSetFile'
74
+ $global :gKVKey_ADAppIdKey = " PsAutoTestAppUsingCertAppId"
75
+
76
+
77
+ Function Check-LoggedInContext ()
78
+ {
79
+ if ($gLoggedInCtx -eq $null )
80
+ {
81
+ Write-Error " '$global :gPsAutoTestSubscriptionName ' subcription does not exist in the list of available subscriptions. Make sure to have it to run the tests"
82
+ Exit
83
+ }
84
+ }
85
+
86
+ Function Get-AutomationTestCertificate ()
87
+ {
88
+ [OutputType ([System.Security.Cryptography.X509Certificates.X509Certificate ])]
89
+ Param ($cert )
90
+
91
+ if ($gLoggedInCtx -ne $null )
92
+ {
93
+ # Download Certificate from KeyVault
94
+ $downloadedCertPath = Download- TestCertificateFromKeyVault
95
+
96
+ # Install Certificate locally
97
+ Install-TestCertificateOnMachine $downloadedCertPath
98
+
99
+ # Verify it was installed locally
100
+ $cert = Get-LocalCertificate
101
+ if ($cert -eq $null )
102
+ {
103
+ throw [System.ApplicationException ] " Unable to retrieve Automation Test Certificate '$gLocalCertSubjectName '"
104
+ }
105
+ }
106
+ return $cert
107
+ }
108
+
109
+ Function Get-LocalCertificate ()
110
+ {
111
+ # TODO: Handle case whgere we get multiple test certificates with exactly the same subject
112
+ $cert = Get-ChildItem $gLocalCertStore | Where-Object {$_.Subject -eq $Global :gLocalCertSubjectName }
113
+ if ($cert -eq $null )
114
+ {
115
+ Log- Info " Trying to find certificate in LocalMachine"
116
+ $cert = Get-ChildItem ' Cert:\LocalMachine\My' | Where-Object {$_.Subject -eq $Global :gLocalCertSubjectName }
117
+ }
118
+
119
+ return $cert
120
+ }
121
+
122
+ Function Download-PublishSettingsFileFromKv ([string ] $localFilePathToDownload )
123
+ {
124
+ $dirPath = [System.IO.Path ]::GetDirectoryName($localFilePathToDownload )
125
+ $pubFileSecContents = Get-AzureKeyVaultSecret - VaultName $global :gVaultName - Name $global :gkvSecKey_PubSettingFileNameKey
126
+ Log- Info " Ready to download Publishsettings file to '$localFilePathToDownload ' from Azure KeyVault"
127
+
128
+ if ([System.IO.Directory ]::Exists($dirPath ) -eq $true )
129
+ {
130
+ [System.IO.File ]::WriteAllText($localFilePathToDownload , $pubFileSecContents.SecretValueText )
131
+ }
132
+ else
133
+ {
134
+ throw [System.IO.DirectoryNotFoundException ] " $dirPath does not exists"
135
+ }
136
+
137
+ Log- Info " Successfully downloaded Publishsettings file to '$localFilePathToDownload '"
138
+ }
139
+
140
+ Function Install-TestCertificateOnMachine ([string ] $localCertPath )
141
+ {
142
+ # $certContentType = "application/x-pkcs12"
143
+ if ([System.IO.File ]::Exists($localCertPath ) -ne $true )
144
+ {
145
+ $localCertPath = Download- TestCertificateFromKeyVault
146
+ }
147
+
148
+ $pwd = Get-LocalCertificatePassword
149
+ $secCertPwd = ConvertTo-SecureString - String $pwd - AsPlainText - Force
150
+
151
+ Log- Info " Ready to install certificate to '$global :gLocalCertStore '"
152
+ Import-PfxCertificate - FilePath $localCertPath - CertStoreLocation $gLocalCertStore - Password $secCertPwd
153
+
154
+ $installedCert = Get-LocalCertificate
155
+ if ($installedCert -eq $null )
156
+ {
157
+ throw [System.ApplicationException ] " Unable to retrieve installed certificate for running automation test"
158
+ }
159
+ else
160
+ {
161
+ Log- Info " Successfully installed certificate to '$global :gLocalCertStore '"
162
+ }
163
+ }
164
+
165
+ Function Download-TestCertificateFromKeyVault ()
166
+ {
167
+
168
+ Check- LoggedInContext
169
+ $kvCertSecret = Get-AzureKeyVaultSecret - VaultName $global :gVaultName - Name $global :kvSecKey_PsAutoTestCertNameKey
170
+
171
+ # ToDo: Handle the case where access denied to keyVault will result in an attempt to create a new certificate (which will fail again, but needs to be handled)
172
+ # if($kvCertSecret -eq $null)
173
+ # {
174
+ # Log-Info "Unable to get certificate from Azure KeyVault"
175
+ # $kvCertSecret = Create-TestAutoCertificateInKv $gVaultName $kvSecKey_PsAutoTestCertNameKey
176
+ # }
177
+
178
+ # Once we create certificate and get it, we store it locally
179
+ if ($kvCertSecret -ne $null )
180
+ {
181
+ $kvSecretBytes = [System.Convert ]::FromBase64String($kvCertSecret.SecretValueText )
182
+ $certCollection2 = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
183
+ $certCollection2.Import ($kvSecretBytes , $null , [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags ]::Exportable)
184
+
185
+ # Save Pfx locally
186
+ $pwd = Get-LocalCertificatePassword
187
+ $certPwdProtectedInBytes = $certCollection2.Export ([System.Security.Cryptography.X509Certificates.X509ContentType ]::Pkcs12, $pwd )
188
+ $pfxLocalFilePath = [System.IO.Path ]::Combine($global :localPfxDirPath , $global :gPfxLocalFileName )
189
+ [System.IO.File ]::WriteAllBytes($pfxLocalFilePath , $certPwdProtectedInBytes )
190
+
191
+ # We are not going the .cer route for now, rather use publishSettings file
192
+ # Save .cer file location to be uploaded to the management portal
193
+ # $kvAutoTestCert = Get-AzureKeyVaultCertificate -VaultName $vaultName -Name $certName
194
+ # $cerLocalFilePath = [Environment]::GetFolderPath("Desktop") + "\PsAutoTestCert.cer"
195
+ # $certBytes = $kvAutoTestCert.Certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
196
+ # [System.IO.File]::WriteAllBytes($cerLocalFilePath, $certBytes)
197
+
198
+ Log- Info " Successfully downloaded certificate at '$pfxLocalFilePath '"
199
+ }
200
+
201
+ return $pfxLocalFilePath
202
+ }
203
+
204
+ Function Get-LocalCertificatePassword ()
205
+ {
206
+ if ([string ]::IsNullOrEmpty($global :gCertPwd ) -eq $true )
207
+ {
208
+ $global :gCertPwd = Read-Host " Please enter certificate password that is ready to be installed on your machine"
209
+ }
210
+
211
+ return $global :gCertPwd
212
+ }
213
+
214
+ Function Login-AzureRMWithCertificate ([bool ]$runOnCIMachine = $false )
215
+ {
216
+ $appId = $global :gPsAutoTestADAppId
217
+
218
+ $localCert = Get-LocalCertificate
219
+ if ($localCert -eq $null )
220
+ {
221
+ If ($runOnCIMachine -eq $false )
222
+ {
223
+ $global :ErrorActionPreference = " SilentlyContinue" # the very first time you install cert, you get an error when you login
224
+ # we could not find certificate installed locally, get it from KeyVault
225
+ Login- InteractivelyAndSelectTestSubscription
226
+ $localCert = Get-AutomationTestCertificate
227
+ # TODO: Handle the case where local certificate is available, but for some reason Service Principal is not available
228
+ }
229
+ else
230
+ {
231
+ throw [System.ApplicationException ] " Local Certificate missing on machine and 'runOnCIMachine' is passed as $runOnCIMachine "
232
+ }
233
+ }
234
+
235
+ $thumbprint = $localCert.Thumbprint
236
+ # Seen cases where for some odd reason, $thumb is being evaluated as object rather than a string, but this does not happen consistently
237
+ $thumbStr = [System.Convert ]::ToString($thumbprint.ToString ())
238
+
239
+ # Update the LoggedInCtx
240
+ $gLoggedInCtx = Login- AzureRmAccount - ServicePrincipal - CertificateThumbprint $thumbStr - ApplicationId $appId - TenantId $global :gTenantId
241
+ $global :ErrorActionPreference = " Stop" # setting it back to default option that were set in Test-Setup
242
+ }
243
+
244
+ Function Login-AzureWithCertificate ()
245
+ {
246
+ $fullPath = [System.IO.Path ]::Combine($PSScriptRoot , $global :gpubSettingLocalFileName )
247
+
248
+ if ([System.IO.File ]::Exists($fullPath ) -ne $true )
249
+ {
250
+ Download- PublishSettingsFileFromKv $fullPath
251
+ }
252
+
253
+ Import-AzurePublishSettingsFile - PublishSettingsFile $fullPath
254
+ }
255
+
256
+ Function Login-InteractivelyAndSelectTestSubscription ()
257
+ {
258
+ Log- Info " Login interactively....."
259
+ $global :gLoggedInCtx = Login- AzureRmAccount
260
+
261
+ Check- LoggedInContext
262
+ Log- Info " Selecting '$global :gPsAutoTestSubscriptionId ' subscription"
263
+ $global :gLoggedInCtx = Select-AzureRmSubscription - SubscriptionId $global :gPsAutoTestSubscriptionId
264
+
265
+ return $global :gLoggedInCtx
266
+ }
267
+
268
+ Function Login-Azure ([bool ]$deleteLocalCertificate = $false , [bool ]$runOnCIMachine = $false )
269
+ {
270
+ try
271
+ {
272
+ Remove-AllSubscriptions
273
+
274
+ if ($deleteLocalCertificate -eq $true )
275
+ {
276
+ Delete- LocalCertificate
277
+ }
278
+
279
+ Login- AzureRMWithCertificate $runOnCIMachine
280
+ Select-AzureRmSubscription - SubscriptionId $global :gPsAutoTestSubscriptionId - TenantId $global :gTenantId
281
+
282
+ Login- AzureWithCertificate
283
+ Select-AzureSubscription - SubscriptionId $global :gPsAutoTestSubscriptionId - Current
284
+ }
285
+ finally
286
+ {
287
+ Delete- DownloadedCertAndPubSetting
288
+ }
289
+ }
290
+
291
+ Function Log-Info ([string ] $info )
292
+ {
293
+ $info = [string ]::Format(" [INFO]: {0}" , $info )
294
+ Write-Host $info - ForegroundColor Yellow
295
+ }
296
+
297
+ Function Log-Error ([string ] $errorInfo )
298
+ {
299
+ $errorInfo = [string ]::Format(" [INFO]: {0}" , $errorInfo )
300
+ Write-Error - Message $errorInfo
301
+ }
302
+
303
+ Function Delete-LocalCertificate ()
304
+ {
305
+ $cert = Get-LocalCertificate
306
+ if ($cert -ne $null )
307
+ {
308
+ $certPath = $cert.PSPath
309
+ Log- Info " Deleting local certificate $certPath "
310
+ Remove-Item $cert.PSPath
311
+ }
312
+ }
313
+
314
+ Function Delete-DownloadedCertAndPubSetting
315
+ {
316
+ $pfxFilePath = [System.IO.Path ]::Combine($global :localPfxDirPath , $global :gPfxLocalFileName )
317
+ if ([System.IO.File ]::Exists($pfxFilePath ) -eq $true )
318
+ {
319
+ # Log-Info "Deleting '$pfxFilePath'"
320
+ Remove-Item $pfxFilePath
321
+ }
322
+
323
+ $pubSettingFile = [System.IO.Path ]::Combine($PSScriptRoot , $global :gpubSettingLocalFileName )
324
+ if ([System.IO.File ]::Exists($pubSettingFile ) -eq $true )
325
+ {
326
+ # Log-Info "Deleting '$pubSettingFile'"
327
+ Remove-Item $pubSettingFile
328
+ }
329
+ }
0 commit comments