├── ModuleIcon.png ├── AzureKeyVaultPasswordRepo ├── AzureKeyVaultPasswordRepo.psd1 ├── en │ └── AzureKeyVaultPasswordRepo.psm1-Help.xml └── AzureKeyVaultPasswordRepo.psm1 └── README.md /ModuleIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tyconsulting/AzureKeyVaultPasswordRepo-PSModule/HEAD/ModuleIcon.png -------------------------------------------------------------------------------- /AzureKeyVaultPasswordRepo/AzureKeyVaultPasswordRepo.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tyconsulting/AzureKeyVaultPasswordRepo-PSModule/HEAD/AzureKeyVaultPasswordRepo/AzureKeyVaultPasswordRepo.psd1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AzureKeyVaultPasswordRepo PowerShell Module 2 | ##Description 3 | This repository contains the source code of the AzureKeyVaultPasswordRepo PowerShell module. AzureKeyVaultPasswordRepo PS module provides a PowerShell console based menu driven interface for users to manage password-based credentials in an Azure Key Vault. 4 | 5 | ##Install Instruction 6 | ###Install from PowerShell Gallery 7 | Install-module AzureKeyVaultPasswordRepo 8 | 9 | ###Manually Install 10 | Download this module from github, and place the AzureKeyVaultPasswordRepo module folder to 'C:\Program Files\WindowsPowerShell\Modules' 11 | 12 | ###Download from PowerShell Gallery 13 | Find-Module AzureKeyVaultPasswordRepo | Save-Module -Force -Path 'C:\Temp' 14 | 15 | ##PowerShell functions 16 | ###Invoke-AzureKeyVaultPasswordRepository 17 | Launch Azure Key Vault Password Repository 18 | 19 | Use Get-Help Invoke-AzureKeyVaultPasswordRepository -Full to access the help file for this function. 20 | 21 | ####Aliases 22 | * ipr 23 | * Start-PasswordRepo 24 | 25 | ##Additional information: 26 | ###PowerShell Gallery: 27 | https://www.powershellgallery.com/packages/AzureKeyVaultPasswordRepo 28 | -------------------------------------------------------------------------------- /AzureKeyVaultPasswordRepo/en/AzureKeyVaultPasswordRepo.psm1-Help.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | Invoke-AzureKeyVaultPasswordRepository 14 | 15 | Launch Azure Key Vault Password Repository 16 | 17 | 18 | 19 | 20 | Invoke 21 | AzureKeyVaultPasswordRepository 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Invoke-AzureKeyVaultPasswordRepository 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | None 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | System.Object 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | EXAMPLE 1 66 | 67 | 68 | 69 | C:\PS> Invoke-AzureKeyVaultPasswordRepository 70 | 71 | Launch Azure Key Vault Password Repository 72 | 73 | 74 | 75 | EXAMPLE 2 76 | 77 | 78 | 79 | C:\PS> ipr 80 | 81 | Launch Azure Key Vault Password Repository using alias 'ipr' 82 | 83 | 84 | 85 | EXAMPLE 3 86 | 87 | 88 | 89 | C:\PS> Start-PasswordRepo 90 | 91 | Launch Azure Key Vault Password Repository using alias 'Start-PasswordRepo' 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /AzureKeyVaultPasswordRepo/AzureKeyVaultPasswordRepo.psm1: -------------------------------------------------------------------------------- 1 | Function New-Passowrd 2 | { 3 | [OutputType([System.Security.SecureString])] 4 | [CmdletBinding()] 5 | PARAM ( 6 | [Parameter(Mandatory = $true)][int]$Length, 7 | [Parameter(Mandatory = $true)][int]$NumberOfSpecialCharacters 8 | ) 9 | Add-Type -AssemblyName System.Web 10 | $PwString = [Web.Security.Membership]::GeneratePassword($Length,$NumberOfSpecialCharacters) 11 | $secString = New-Object System.Security.SecureString 12 | For ($i = 0; $i -lt $PwString.length; $i++) 13 | { 14 | $char = $PwString.Substring($i, 1) 15 | $secString.AppendChar($char) 16 | } 17 | $secString 18 | } 19 | Function Get-ExistingCred 20 | { 21 | [OutputType([System.Collections.ArrayList])] 22 | [CmdletBinding()] 23 | PARAM ( 24 | [Parameter(Mandatory = $false)][string]$SearchString = $null 25 | ) 26 | #Get existing secrets from key vault 27 | If ($PSBoundParameters.ContainsKey('SearchString')) 28 | { 29 | $ExistingSecrets = Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName| 30 | Where-Object -FilterScript { 31 | $_.Tags.ContainsKey('CredName') 32 | } | 33 | Where-Object -FilterScript { 34 | $_.Tags['CredName'] -imatch $SearchString 35 | } 36 | } 37 | else 38 | { 39 | $ExistingSecrets = Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName 40 | } 41 | 42 | #Get credential names 43 | $ExistingCredNames = $ExistingSecrets | 44 | ForEach-Object -Process { 45 | $_.tags['CredName'] 46 | } | 47 | Get-Unique 48 | #Get credential user names 49 | $arrExistingCreds = New-Object -TypeName System.Collections.ArrayList 50 | FOreach ($item in $ExistingCredNames) 51 | { 52 | $UserNameSecret = $ExistingSecrets | Where-Object -FilterScript { 53 | $_.Tags['CredName'] -eq $item -and $_.Tags['Type'] -eq 'UserName' 54 | } 55 | $PasswordSecret = $ExistingSecrets | Where-Object -FilterScript { 56 | $_.Tags['CredName'] -eq $item -and $_.Tags['Type'] -eq 'Password' 57 | } 58 | $UserName = (Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $UserNameSecret.Name).SecretValueText 59 | $objExistingCred = New-Object -TypeName psobject -Property @{ 60 | CredName = $item 61 | UserName = $UserName 62 | UserNameSecretName = $UserNameSecret.Name 63 | PasswordSecretName = $PasswordSecret.Name 64 | } 65 | [void]$arrExistingCreds.Add($objExistingCred) 66 | } 67 | ,$arrExistingCreds 68 | } 69 | Function New-KeyVaultCred 70 | { 71 | $NewCredName = $null 72 | $NewCredUserName = $null 73 | Do 74 | { 75 | $NewCredName = Read-Host -Prompt 'Give your new credential a name (only contain alphanumeric characters and dash)' 76 | } 77 | while ($NewCredName.Length -eq 0 -or $NewCredName -notmatch '^[0-9a-zA-Z-]+$') 78 | Do 79 | { 80 | $NewCredUserName = Read-Host -Prompt 'Enter user name' 81 | } 82 | while ($NewCredUserName.Length -eq 0) 83 | $NewCredPassword = Read-Host -Prompt "Enter new password or hit enter to generate a random password with $Global:RandomPasswordLength character in length with $Global:RandomPasswordSpecialCharactersCount special characters" -AsSecureString 84 | $UserNameSecretName = "$NewCredName`-UserName" 85 | $PasswordSecretName = "$NewCredName`-Password" 86 | 87 | #preparing for the tags 88 | $UTags = @{ 89 | CredName = "$NewCredName" 90 | Type = 'UserName' 91 | } 92 | $PTags = @{ 93 | CredName = "$NewCredName" 94 | Type = 'Password' 95 | } 96 | 97 | #Create secrets 98 | Write-Output -InputObject 'Creating Credential in key vault. please wait...' 99 | #user name 100 | $NewCredUserNameSecString = New-Object System.Security.SecureString 101 | For ($i = 0; $i -lt $NewCredUserName.length; $i++) 102 | { 103 | $char = $NewCredUserName.Substring($i, 1) 104 | $NewCredUserNameSecString.AppendChar($char) 105 | } 106 | $NewUserSecret = Set-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $UserNameSecretName -SecretValue $NewCredUserNameSecString -Tag $UTags -ErrorVariable errCreateNewUserName 107 | #password secret 108 | $bNewCopyPassworToClipboard = $false 109 | If ($NewCredPassword.length -eq 0) 110 | { 111 | Write-Output -InputObject 'Password not specified. Generating a new password...' 112 | $NewCredPassword = New-Passowrd -Length $Global:RandomPasswordLength -NumberOfSpecialCharacters $Global:RandomPasswordSpecialCharactersCount 113 | $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($NewCredPassword) 114 | $NewCredPasswordClearText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) 115 | $bNewCopyPassworToClipboard = $true 116 | } 117 | $NewPasswordSecret = Set-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $PasswordSecretName -SecretValue $NewCredPassword -Tag $PTags -ErrorVariable errCreateNewPassword 118 | If ($errCreateNewUserName.Count -eq 0 -and $errCreateNewPassword.Count -eq 0) 119 | { 120 | Write-Host -Object 'Credential successfully created.' -ForegroundColor Green 121 | if ($bNewCopyPassworToClipboard -eq $true) 122 | { 123 | $NewCredPasswordClearText | clip.exe 124 | Write-Host -Object 'Password copied to clipboard.' -ForegroundColor Green 125 | Write-output "" 126 | } 127 | } 128 | Write-Output -InputObject '' 129 | } 130 | 131 | Function Get-KeyVaultCred 132 | { 133 | [CmdletBinding()] 134 | PARAM ( 135 | [Parameter(Mandatory = $false)][switch]$Search 136 | ) 137 | If ($Search) 138 | { 139 | Write-Host -Object 'Search credential' -ForegroundColor Yellow 140 | Do 141 | { 142 | $SearchString = Read-Host -Prompt 'Enter search string (only contain alphanumeric characters and dash)' 143 | } 144 | while ($SearchString.Length -eq 0 -or $SearchString -notmatch '^[0-9a-zA-Z-]+$') 145 | Write-Output -InputObject '' 146 | $ExistingCreds = Get-ExistingCred -SearchString $SearchString 147 | } 148 | else 149 | { 150 | $ExistingCreds = Get-ExistingCred 151 | } 152 | 153 | If ($ExistingCreds.count -gt 0) 154 | { 155 | Write-Output -InputObject "Number of existing credential(s) found in Azure Key Vault: $($ExistingCreds.count)" 156 | Write-Host -Object 'Select credential:' -ForegroundColor DarkGray 157 | $i = 0 158 | Write-Host -Object " $i. Go Back" -ForegroundColor DarkGray 159 | Foreach ($item in $ExistingCreds) 160 | { 161 | $i++ 162 | Write-Host -Object " $i. $($item.CredName) `($($item.UserName)`)" -ForegroundColor Yellow 163 | } 164 | Do 165 | { 166 | [int]$ans = Read-Host -Prompt "Enter selection (0 - $i)" 167 | if ($ans -gt 0 -and $ans -le $i) 168 | { 169 | Write-Output -InputObject '' 170 | #retrieve the cred 171 | $SelectedCred = $ExistingCreds[$ans-1] 172 | 173 | Do 174 | { 175 | Write-Host -Object "Selected credential name: $($SelectedCred.CredName)" -ForegroundColor DarkGray 176 | Write-Host -Object ' 0. Go Back' -ForegroundColor DarkGray 177 | Write-Host -Object ' 1. Copy User Name to clipboard' -ForegroundColor Yellow 178 | Write-Host -Object ' 2. Copy Password to clipboard' -ForegroundColor Yellow 179 | Write-Host -Object ' 3. Update Credential' -ForegroundColor Yellow 180 | Write-Host -Object ' 4. Delete Credential' -ForegroundColor Yellow 181 | Do 182 | { 183 | [int]$GetCredOption = Read-Host -Prompt 'Enter selection (0 - 4)' 184 | } 185 | Until ($GetCredOption -ge 0 -and $GetCredOption -le 4) 186 | Write-Output -InputObject '' 187 | If ($GetCredOption -eq 1) #Copy username to clipboard 188 | { 189 | $ExistingCredUserName = (Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $SelectedCred.UserNameSecretName).SecretValueText 190 | $ExistingCredUserName | clip.exe 191 | Write-Host -Object 'User Name copied to clipboard.' -ForegroundColor Green 192 | Write-output "" 193 | Write-Output -InputObject '' 194 | } 195 | elseif ($GetCredOption -eq 2) #Copy password to clipboard 196 | { 197 | $ExistingCredPassword = (Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $SelectedCred.PasswordSecretName).SecretValueText 198 | $ExistingCredPassword | clip.exe 199 | Write-Host -Object 'Password copied to clipboard.' -ForegroundColor Green 200 | Write-output "" 201 | } 202 | elseif ($GetCredOption -eq 3) #Update 203 | { 204 | $UpdatedUserName = Read-Host -Prompt 'Enter new user name or hit enter to keep the existing user name' 205 | $UpdatedPassword = Read-Host -Prompt "Enter new password or hit enter to generate a random password with $Global:RandomPasswordLength character in length with $Global:RandomPasswordSpecialCharactersCount special characters" -AsSecureString 206 | $bEditCopyPassworToClipboard = $false 207 | If ($UpdatedPassword.length -eq 0) 208 | { 209 | Write-Output -InputObject 'Password not specified. Generating a new password...' 210 | $UpdatedPassword = New-Passowrd -Length $Global:RandomPasswordLength -NumberOfSpecialCharacters $Global:RandomPasswordSpecialCharactersCount 211 | $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($UpdatedPassword) 212 | $UpdatedPasswordClearText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) 213 | $bEditCopyPassworToClipboard = $true 214 | } 215 | 216 | Write-Output -InputObject 'Updating Credential in key vault. please wait...' 217 | If ($UpdatedUserName.Length -gt 0) 218 | { 219 | #Upate the user name secret 220 | $UpdatedUserNameSecString = New-Object System.Security.SecureString 221 | For ($i = 0; $i -lt $UpdatedUserName.length; $i++) 222 | { 223 | $char = $UpdatedUserName.Substring($i, 1) 224 | $UpdatedUserNameSecString.AppendChar($char) 225 | } 226 | $ExistingUserNameSecret = Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $SelectedCred.UserNameSecretName 227 | $UpdateUserNameSecret = Set-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $ExistingUserNameSecret.Name -SecretValue $UpdatedUserNameSecString -Tag $($ExistingUserNameSecret.Attributes.Tags) -ErrorVariable errUpdateUserName 228 | } 229 | #Update the password secret 230 | $ExistingPasswordSecret = Get-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $SelectedCred.PasswordSecretName 231 | $UpdatePasswordSecret = Set-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $ExistingPasswordSecret.Name -SecretValue $UpdatedPassword -Tag $($ExistingPasswordSecret.Attributes.Tags) -ErrorVariable errUpdatePassword 232 | If ($errUpdateUserName.Count -eq 0 -and $errUpdatePassword.Count -eq 0) 233 | { 234 | Write-Host -Object 'Credential successfully updated.' -ForegroundColor Green 235 | if ($bEditCopyPassworToClipboard -eq $true) 236 | { 237 | $UpdatedPasswordClearText | clip.exe 238 | Write-Host -Object 'Password copied to clipboard.' -ForegroundColor Green 239 | Write-output "" 240 | } 241 | } 242 | Write-Output -InputObject '' 243 | } 244 | elseif ($GetCredOption -eq 4) #delete 245 | { 246 | #Confirm 247 | $DeleteConfirmationTitle = 'Confirming Credential Deletion' 248 | $DeleteConfirmationMessage = "Are you sure you want to delete credential '$($SelectedCred.CredName)'?" 249 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Delete.' 250 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', 'Keep.' 251 | $DeleteConfirmOptions = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 252 | $DeleteConfirmed = $host.ui.PromptForChoice($DeleteConfirmationTitle, $DeleteConfirmationMessage, $DeleteConfirmOptions, 0) 253 | If ($DeleteConfirmed -eq 0) 254 | { 255 | #Yes selected 256 | #Remove the user name secret 257 | $DeleteUserNameSecret = Remove-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $SelectedCred.UserNameSecretName -Force -Confirm:$false -ErrorVariable errDeleteUserName 258 | #Remove the password secret 259 | $DeletePasswordSecret = Remove-AzureKeyVaultSecret -VaultName $Global:KeyVaultName -Name $SelectedCred.PasswordSecretName -Force -Confirm:$false -ErrorVariable errDeletePassword 260 | If ($errDeleteUserName.Count -eq 0 -and $errDeletePassword.Count -eq 0) 261 | { 262 | Write-Host -Object 'Credential successfully deleted.' -ForegroundColor Green 263 | $ExistingCreds.Remove($SelectedCred) 264 | #Go back to the parent menu 265 | $GetCredOption = 0 266 | } 267 | Write-Output -InputObject '' 268 | } 269 | } 270 | } 271 | Until ($GetCredOption -eq 0) 272 | } 273 | } 274 | while ($ans -lt 0 -or $ans -gt $i) 275 | Write-Output -InputObject '' 276 | } 277 | else 278 | { 279 | Write-Host -Object 'No credentials are found in the Azure Key Vault.' -ForegroundColor Red 280 | } 281 | Write-Output -InputObject '' 282 | } 283 | 284 | Function Save-KeyVaultRepoProfile 285 | { 286 | #confirm 287 | Write-Output -InputObject 'By Saving the selected Azure subscription and Key Vault in registry, you will no need to select them again.' 288 | $SaveProfileConfirmationTitle = 'Save Profile' 289 | $SaveProfileConfirmationMessage = 'Are you sure you want to save selected Azure subscription Id and key vault name in your profile?' 290 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Save.' 291 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', "Don't Save." 292 | $SaveProfileConfirmOptions = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 293 | $SaveProfileConfirmed = $host.ui.PromptForChoice($SaveProfileConfirmationTitle, $SaveProfileConfirmationMessage, $SaveProfileConfirmOptions, 0) 294 | If ($SaveProfileConfirmed -eq 0) 295 | { 296 | #Yes selected 297 | $context = Get-AzureRmContext 298 | #Create a new regkey 299 | $NewRegKey = New-Item -Path $Global:SettingRegPath -Name $context.account.id -Force 300 | $ProfilePath = Join-Path -Path $Global:SettingRegPath -ChildPath $context.account.id 301 | #Save Azure Subscription Id 302 | $AzureSubIdRegValue = New-ItemProperty -Path $ProfilePath -Name AzureSubscriptionId -Value $context.Subscription.SubscriptionId -PropertyType 'String' -Force 303 | $AzureKeyVaultNameRegValue = New-ItemProperty -Path $ProfilePath -Name AzureKeyVaultName -Value $Global:KeyVaultName -PropertyType 'String' -Force 304 | Write-Host -Object 'Profile saved.' -ForegroundColor Green 305 | Write-Output -InputObject '' 306 | } 307 | } 308 | Function Get-KeyVaultRepoProfile 309 | { 310 | $context = Get-AzureRmContext 311 | $RegKeyPath = Join-Path -Path $Global:SettingRegPath -ChildPath $context.Account.Id 312 | If (Test-Path $RegKeyPath) 313 | { 314 | Try 315 | { 316 | $regvalues = Get-ItemProperty -Path $RegKeyPath -ErrorAction SilentlyContinue 317 | $AzureSubId = $regvalues.AzureSubscriptionId 318 | $KeyVaultName = $regvalues.AzureKeyVaultName 319 | } 320 | catch 321 | { 322 | $AzureSubId = $null 323 | $KeyVaultName = $null 324 | } 325 | } 326 | 327 | If ($AzureSubId.Length -gt 0 -and $KeyVaultName -gt 0) 328 | { 329 | $objProperty = @{ 330 | 'AzureSubId' = $AzureSubId 331 | 'KeyVaultName' = $KeyVaultName 332 | } 333 | $objProfile = New-Object -TypeName psobject -Property $objProperty 334 | $objProfile 335 | } 336 | else 337 | { 338 | $null 339 | } 340 | } 341 | 342 | Function Remove-KeyVaultRepoProfile 343 | { 344 | $context = Get-AzureRmContext 345 | $RegKeyPath = Join-Path -Path $Global:SettingRegPath -ChildPath $context.Account.Id 346 | If (Test-Path $RegKeyPath) 347 | { 348 | #delete the registry 349 | #confirm 350 | Write-Output -InputObject 'By Deleting the Key Vault Password Repository Profile from registry, you will need to manually select Azure Subscription and key vault when you use it next time.' 351 | $DeleteProfileConfirmationTitle = 'Delete Profile' 352 | $DeleteProfileConfirmationMessage = 'Are you sure you want to delete your profile?' 353 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Delete.' 354 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', "Don't Delete." 355 | $DeleteProfileConfirmOptions = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 356 | $DeleteProfileConfirmed = $host.ui.PromptForChoice($DeleteProfileConfirmationTitle, $DeleteProfileConfirmationMessage, $DeleteProfileConfirmOptions, 0) 357 | If ($DeleteProfileConfirmed -eq 0) 358 | { 359 | #Yes selected 360 | $DeleteKey = Remove-Item -Path $RegKeyPath -Recurse -Force -ErrorVariable errDeleteProfile 361 | If ($errDeleteProfile.Count -eq 0) 362 | { 363 | Write-Host -Object 'Profile Deleted.' -ForegroundColor Green 364 | Write-Output -InputObject '' 365 | } 366 | else 367 | { 368 | Write-Error -Message 'Failed to delete the profile.' 369 | } 370 | } 371 | } 372 | else 373 | { 374 | Write-Host -Object "No profile exists for the current user Id '$($context.Account.Id)'." -ForegroundColor Red 375 | } 376 | } 377 | 378 | Function Add-KeyVaultFullAccessPolicy 379 | { 380 | 381 | Do 382 | { 383 | $AADSearchString = Read-Host -Prompt 'Enter a search string for the Azure AD user to search Azure Active Directory (i.e. name)' 384 | $AADUsers = Get-AzureRmADUser -SearchString $AADSearchString 385 | If ($AADUsers.count -eq 0) 386 | { 387 | Write-Host -Object 'No Azure AD users found that matches the search string. Please enter another search string' -ForegroundColor Red 388 | } 389 | } 390 | until ($AADUsers.count -gt 0) 391 | for ($i = 1;$i -le $AADUsers.count; $i++) 392 | { 393 | Write-Host -Object "$i. $($AADUsers[$i-1].DisplayName) `| UPN: $($AADUsers[$i-1].UserPrincipalName) `| Object Id: $($AADUsers[$i-1].Id.ToString())" -ForegroundColor Yellow 394 | } 395 | Write-Host -Object 'Select the User Accountccount' -ForegroundColor Yellow 396 | [int]$ans = Read-Host -Prompt 'Enter selection' 397 | $VaultAdmin = $AADUsers[$ans-1] 398 | $VaultAdminObjectId = $VaultAdmin.Id.ToString() 399 | Write-Output -InputObject "Configuring Key Vault Access Policy for '$($VaultAdmin.UserPrincipalName)'." 400 | Set-AzureRmKeyVaultAccessPolicy -VaultName $Global:KeyVaultName -ObjectId $VaultAdminObjectId -PermissionsToKeys all -PermissionsToSecrets all -ErrorAction errAddAccess 401 | If ($errAddAccess.Count -eq 0) 402 | { 403 | Write-Host -Object 'Key Vault access granted.' -ForegroundColor Green 404 | Write-Output -InputObject '' 405 | } 406 | else 407 | { 408 | Write-Error -Message 'Failed to grant access to the Key Vault.' 409 | Write-Output -InputObject '' 410 | } 411 | } 412 | 413 | Function Remove-KeyVaultFullAccessPolicy 414 | { 415 | #Get keyvault 416 | $KeyVault = Get-AzureRmKeyVault -VaultName $Global:KeyVaultName 417 | #Get Access policies 418 | $AccessPolicies = $KeyVault.AccessPolicies 419 | Write-Host -Object 'Select Account to Remove:' -ForegroundColor DarkGray 420 | $i = 0 421 | Write-Host -Object " $i. Go Back" -ForegroundColor DarkGray 422 | Foreach ($item in $AccessPolicies) 423 | { 424 | $i++ 425 | Write-Host -Object " $i. $($item.DisplayName) `(Object Id: $($item.ObjectId)`)" -ForegroundColor Yellow 426 | } 427 | Do 428 | { 429 | [int]$ans = Read-Host -Prompt "Enter selection (0 - $i)" 430 | if ($ans -gt 0 -and $ans -le $i) 431 | { 432 | $AccessPolicyToRemove = $AccessPolicies[$i-1] 433 | #Confirm 434 | Write-Output -InputObject 'By removing the access to Key Vault, the selected user will no longer have access to the credentials saved in this key vault.' 435 | $DeleteAccessConfirmationTitle = 'Remove Access' 436 | $DeleteAccessConfirmationMessage = "Are you sure you want to remove Key Vault access for '$($item.DisplayName)'?" 437 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Remove.' 438 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', "Don't Remove." 439 | $DeleteAccessConfirmOptions = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 440 | $DeleteAccessConfirmed = $host.ui.PromptForChoice($DeleteAccessConfirmationTitle, $DeleteAccessConfirmationMessage, $DeleteAccessConfirmOptions, 0) 441 | If ($DeleteAccessConfirmed -eq 0) 442 | { 443 | #Yes selected 444 | Write-Output "Removing Key Vault access for '$($AccessPolicyToRemove.DisplayName)'" 445 | $RemoveJob = Remove-AzureRmKeyVaultAccessPolicy -VaultName $Global:KeyVaultName -ObjectId $AccessPolicyToRemove.ObjectId -ErrorVariable errRemoveAccess 446 | If ($errRemoveAccess.Count -eq 0) 447 | { 448 | Write-Host -Object 'Key Vault access removed.' -ForegroundColor Green 449 | Write-Output -InputObject '' 450 | } 451 | else 452 | { 453 | Write-Error -Message 'Failed to remove access to the Key Vault.' 454 | Write-Output -InputObject '' 455 | } 456 | } else { 457 | Write-Output "Key Vault access removal cancelled for '$($AccessPolicyToRemove.DisplayName)'." 458 | Write-Output -InputObject '' 459 | } 460 | } 461 | } 462 | while ($ans -lt 0 -or $ans -gt $i) 463 | } 464 | 465 | # .EXTERNALHELP AzureKeyVaultPasswordRepo.psm1-Help.xml 466 | Function Invoke-AzureKeyVaultPasswordRepository 467 | { 468 | Clear-Host 469 | #region variables 470 | $IdentifyingTagName = 'Purpose' 471 | $IdentifyingTagValue = 'PersonalPasswordRepo' 472 | $Global:RandomPasswordLength = 20 473 | $Global:RandomPasswordSpecialCharactersCount = 3 474 | $TopMenuTitleLine1 = 'Azure Key Vault Personal Password Repository' 475 | $TopMenuTitleLine2 = '============================================' 476 | 477 | $Global:SettingRegPath = 'HKCU:\Software\TYConsulting\AzureKeyVaultPasswordRepo\Profiles' 478 | #endregion 479 | 480 | #region retrieve Azure resources 481 | #Logging in to Azure 482 | Write-Output -InputObject 'Checking for existing Azure login session.' 483 | Do 484 | { 485 | Try 486 | { 487 | $context = Get-AzureRmContext 488 | $CurrentAccount = $context.Account.Id 489 | If ($CurrentAccount -ne $null) 490 | { 491 | $AzureContextTitle = 'Existing Azure Credential' 492 | $ExistingAccountMessage = "You are currently logged in to Azure using account $CurrentAccount. Do you want to keep using this account?" 493 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Keep using this Id.' 494 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', 'Login to Azure using another Id.' 495 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 496 | 497 | $UserSelected = $host.ui.PromptForChoice($AzureContextTitle, $ExistingAccountMessage, $options, 0) 498 | If ($UserSelected -eq 1) 499 | { 500 | Write-Output -InputObject 'Login to Auzre' 501 | $null = Add-AzureRmAccount 502 | $context = Get-AzureRmContext 503 | $CurrentAccount = $context.Account.Id 504 | } 505 | } 506 | else 507 | { 508 | Write-Host -Object 'You are currently not logged in to Azure. Please login.' -ForegroundColor Red 509 | $null = Add-AzureRmAccount 510 | $context = Get-AzureRmContext 511 | $CurrentAccount = $context.Account.Id 512 | } 513 | } 514 | Catch 515 | { 516 | Write-Host -Object 'You are currently not logged in to Azure. Please login.' -ForegroundColor Red 517 | $null = Add-AzureRmAccount 518 | $context = Get-AzureRmContext 519 | $CurrentAccount = $context.Account.Id 520 | } 521 | } 522 | Until ($CurrentAccount -gt 0) 523 | $CurrentSubName = $context.Subscription.SubscriptionName 524 | $CurrentSubId = $context.Subscription.SubscriptionId 525 | 526 | #Check for existing profile 527 | Write-Output -InputObject 'Looking for existing profile' 528 | $ExistingProfile = Get-KeyVaultRepoProfile 529 | If ($ExistingProfile) 530 | { 531 | Write-Output -InputObject "Existing Profile is found for user '$($context.Account.Id)'. Loading profile..." 532 | if ($CurrentSubId -ine $ExistingProfile.AzureSubId) 533 | { 534 | Write-Output "Setting Azure Subscription '$($ExistingProfile.AzureSubId)' to the context." 535 | $null = Set-AzureRmContext -SubscriptionId $ExistingProfile.AzureSubId 536 | } 537 | Write-Output "Connecting to key vault '$($ExistingProfile.KeyVaultName)'" 538 | $KeyVault = Get-AzureRmKeyVault -VaultName $ExistingProfile.KeyVaultName 539 | $Global:KeyVaultName = $KeyVault.VaultName 540 | } 541 | else 542 | { 543 | Write-Host -Object "No Profile is found for user '$($context.Account.Id)'" -ForegroundColor Red 544 | #Select Azure subscription 545 | If ($CurrentSubName -ne $null) 546 | { 547 | $SelectSubTitle = 'Select Azure Subscription' 548 | $SelectSubMessage = "Currently the Azure subscription '$CurrentSubName (Id: $CurrentSubId)' is selected. Do you want to use this subscription?" 549 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Use the current subscription.' 550 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', 'Select another subscription.' 551 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 552 | 553 | $UserSelected = $host.ui.PromptForChoice($SelectSubTitle, $SelectSubMessage, $options, 0) 554 | } 555 | If($UserSelected -eq 1) 556 | { 557 | Write-Output -InputObject 'Getting Azure subscriptions...' 558 | $subscriptions = Get-AzureRmSubscription -WarningAction SilentlyContinue 559 | if ($subscriptions.count -gt 0) 560 | { 561 | Write-Host -Object 'Select Azure Subscription of which the Azure Key Vault is located' -ForegroundColor DarkGray 562 | 563 | $menu = @{} 564 | for ($i = 1;$i -le $subscriptions.count; $i++) 565 | { 566 | Write-Host -Object "$i. $($subscriptions[$i-1].SubscriptionName)" -ForegroundColor Yellow 567 | $menu.Add($i,($subscriptions[$i-1].SubscriptionId)) 568 | } 569 | Do 570 | { 571 | [int]$ans = Read-Host -Prompt "Enter selection (1 - $($i -1))" 572 | } 573 | while ($ans -le 0 -or $ans -gt $($i -1)) 574 | Write-Output -InputObject '' 575 | $subscriptionID = $menu.Item($ans) 576 | $null = Set-AzureRmContext -SubscriptionId $subscriptionID 577 | } 578 | else 579 | { 580 | Write-Error -Message 'No Azure Subscription found. Unable to continue!' 581 | Exit -1 582 | } 583 | } 584 | $context = Get-AzureRmContext 585 | $subscriptionID = $context.Subscription.SubscriptionId 586 | #Check for existing key vault configured for AaaS solutions 587 | $ExistingKeyVaults = Get-AzureRmKeyVault | 588 | Where-Object -FilterScript { 589 | $_.Tags.ContainsKey($IdentifyingTagName) 590 | } | 591 | Where-Object -FilterScript { 592 | $_.Tags["$IdentifyingTagName"] -eq $IdentifyingTagValue 593 | } 594 | Write-Output -InputObject '' 595 | if ($ExistingKeyVaults.count -eq 0) 596 | { 597 | Write-Host -Object 'No existing AaaS key vault detected. select 0 to create a new key vault.' -ForegroundColor Red 598 | } 599 | else 600 | { 601 | Write-Host -Object 'Select a Key Vault' -ForegroundColor DarkGray 602 | } 603 | Write-Host -Object '0. [Create New Key Vault]' -ForegroundColor DarkGray 604 | $i = 0 605 | Foreach ($KV in $ExistingKeyVaults) 606 | { 607 | $i++ 608 | Write-Host -Object "$i. $($KV.VaultName)" -ForegroundColor Yellow 609 | } 610 | Do 611 | { 612 | [int]$KVAnswer = Read-Host -Prompt "Enter selection (0 - $i)" 613 | } 614 | while ($KVAnswer -lt 0 -or $KVAnswer -gt $i) 615 | Write-Output -InputObject '' 616 | If ($KVAnswer -eq 0) 617 | { 618 | #Create new key vault 619 | $AzureLocations = Get-AzureRmLocation 620 | $ExistingResourceGroups = @() 621 | #get resource groups 622 | Foreach ($item in Get-AzureRmResourceGroup) 623 | { 624 | $RGLoc = $AzureLocations | Where-Object -FilterScript { 625 | $item.Location -eq $_.Location 626 | } 627 | if ($RGLoc.Providers -contains 'Microsoft.KeyVault') 628 | { 629 | $ExistingResourceGroups += $item 630 | } 631 | } 632 | 633 | Write-Host -Object 'Select a resource group' -ForegroundColor DarkGray 634 | Write-Host -Object '0. [Create New Resource Group]' -ForegroundColor DarkGray 635 | $i = 0 636 | Foreach ($RG in $ExistingResourceGroups) 637 | { 638 | $i++ 639 | Write-Host -Object "$i. $($RG.ResourceGroupName)" -ForegroundColor Yellow 640 | } 641 | Do 642 | { 643 | [int]$RGAnswer = Read-Host -Prompt "Enter selection (1 - $i)" 644 | } 645 | while ($RGAnswer -lt 0 -or $RGAnswer -gt $i) 646 | Write-Output -InputObject '' 647 | 648 | if ($RGAnswer -eq 0) 649 | { 650 | #Create a new resource group 651 | #resource group name 652 | Do 653 | { 654 | $ResourceGroupName = Read-Host -Prompt 'Enter the name for the new Resource Group (only include alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.)' 655 | } 656 | while ($ResourceGroupName.Length -eq 0 -or $ResourceGroupName -notmatch '^[-\w\._\(\)]+$') 657 | #location 658 | $AvailableLocations = $AzureLocations | 659 | Where-Object -FilterScript { 660 | $_.Providers -contains 'Microsoft.Resources' -and $_.Providers -contains 'Microsoft.KeyVault' 661 | } | 662 | Sort-Object -Property DisplayName 663 | Write-Host -Object 'Select Azure Region:' -ForegroundColor DarkGray 664 | $i = 0 665 | foreach ($loc in $AvailableLocations) 666 | { 667 | $i++ 668 | Write-Host -Object "$i. $($loc.DisplayName)" -ForegroundColor Yellow 669 | } 670 | Do 671 | { 672 | [int]$ans = Read-Host -Prompt "Enter selection (1 - $i)" 673 | } 674 | while ($ans -le 0 -or $ans -gt $i) 675 | Write-Output -InputObject '' 676 | $Location = $AvailableLocations[$ans-1] 677 | $strLocation = $Location.Location 678 | Write-Output -InputObject "Creating new Resource Group '$ResourceGroupName' in Azure region '$($Location.DisplayName)'." 679 | $NewRG = New-AzureRmResourceGroup -Name $ResourceGroupName -Location $strLocation 680 | If ($NewRG.ProvisioningState -eq 'Succeeded') 681 | { 682 | Write-Host -Object "Resource group '$ResourceGroupName' successfully created." -ForegroundColor Green 683 | } 684 | else 685 | { 686 | Write-Host -Object "Resource Group creation failed. provisioning state: '$($NewRG.ProvisioningState)'." -ForegroundColor Red 687 | } 688 | Write-Output -InputObject '' 689 | } 690 | else 691 | { 692 | #choose existing Resource Group 693 | $ResourceGroupName = $ExistingResourceGroups[$RGAnswer -1].ResourceGroupName 694 | $strLocation = $ExistingResourceGroups[$RGAnswer -1].Location 695 | Write-Output -InputObject '' 696 | } 697 | 698 | #Create Key Vault 699 | #Key Vault name 700 | Do 701 | { 702 | $Global:KeyVaultName = Read-Host -Prompt 'Enter the name for the new Key Vault (only include alphanumeric characters and dashes and cannot start with a number.)' 703 | } 704 | while ($Global:KeyVaultName.Length -eq 0 -or $Global:KeyVaultName -notmatch '^[a-zA-Z0-9-]{3,24}$') 705 | Write-Output -InputObject 'Creating Azure Key Vault' 706 | $KeyVault = New-AzureRmKeyVault -VaultName $Global:KeyVaultName -ResourceGroupName $ResourceGroupName -Location $strLocation -EnabledForDeployment -EnabledForTemplateDeployment -EnabledForDiskEncryption -Sku Standard -Tag @{ 707 | $IdentifyingTagName = $IdentifyingTagValue 708 | } -Confirm:$false 709 | $Global:KeyVaultName = $KeyVault.VaultName 710 | 711 | #Give someone access to the key vault 712 | Write-Output -InputObject 'Assigning Azure Key Vault permission to an Azure AD user. Searching Azure AD to get the AD user.' 713 | Add-KeyVaultFullAccessPolicy 714 | } 715 | else 716 | { 717 | $KeyVault = $ExistingKeyVaults[$KVAnswer-1] 718 | $Global:KeyVaultName = $KeyVault.VaultName 719 | } 720 | } 721 | 722 | #endregion 723 | 724 | #region manage key vault secrets 725 | Clear-Host 726 | Do 727 | { 728 | Write-Host -Object $TopMenuTitleLine1 -ForegroundColor Cyan 729 | Write-Host -Object $TopMenuTitleLine2 -ForegroundColor Cyan 730 | Write-Host -Object " Selected Key Vault: '$Global:KeyVaultName'" -ForegroundColor Cyan 731 | Write-Host -Object "Select what you'd like to do:" -ForegroundColor DarkGray 732 | Write-Host -Object '1. Create new credential' -ForegroundColor Yellow 733 | Write-Host -Object '2. Choose existing credentials from the list' -ForegroundColor Yellow 734 | Write-Host -Object '3. Search existing credentials' -ForegroundColor Yellow 735 | Write-Host -Object '4. Save Azure subscription and Key Vault selection in Key Vault Password Repository profile' -ForegroundColor Yellow 736 | Write-Host -Object '5. Delete Key Vault Password Repository profile' -ForegroundColor Yellow 737 | Write-Host -Object '6. Grant Key Vault Access' -ForegroundColor Yellow 738 | Write-Host -Object '7. Remove Key Vault Access' -ForegroundColor Yellow 739 | Write-Host -Object '8. Exit' -ForegroundColor DarkGray 740 | 741 | Do 742 | { 743 | [int]$option = Read-Host -Prompt 'Enter selection (1 - 8)' 744 | } 745 | while ($option -le 0 -or $option -gt 8) 746 | Write-Output -InputObject '' 747 | SWITCH ($option) 748 | { 749 | 1 750 | { 751 | #Create new 752 | New-KeyVaultCred 753 | Write-Output -InputObject '' 754 | } 755 | 2 756 | { 757 | #list existing 758 | Get-KeyVaultCred 759 | } 760 | 3 761 | { 762 | #search existing 763 | Get-KeyVaultCred -Search 764 | } 765 | 4 766 | { 767 | #Save profile 768 | Save-KeyVaultRepoProfile 769 | } 770 | 5 771 | { 772 | #Delete profile 773 | Remove-KeyVaultRepoProfile 774 | } 775 | 6 776 | { 777 | #Grant access 778 | Add-KeyVaultFullAccessPolicy 779 | } 780 | 7 781 | { 782 | #Grant access 783 | Remove-KeyVaultFullAccessPolicy 784 | } 785 | 8 786 | { 787 | Write-Output -InputObject 'See you next time!' 788 | } 789 | } 790 | } 791 | Until ($option -eq 8) 792 | } 793 | 794 | New-Alias -Name ipr -Value Invoke-AzureKeyVaultPasswordRepository 795 | New-Alias -Name Start-PasswordRepo -Value Invoke-AzureKeyVaultPasswordRepository 796 | Export-ModuleMember -Alias * -Function * 797 | --------------------------------------------------------------------------------