Framework/Core/CredHygiene/CredHygieneScript.ps1
Set-StrictMode -Version Latest class CredHygiene : CommandBase{ [string] $credName; [string] $credLocation; [int] $rotationInt; [string] $alertPhoneNumber = ""; [string] $alertEmail; [string] $comment; hidden [string] $AzSKTemp = (Join-Path $([Constants]::AzSKAppFolderPath) $([Constants]::RotationMetadataSubPath)); hidden [PSObject] $AzSKResourceGroup = $null; hidden [PSObject] $AzSKStorageAccount = $null; hidden [PSObject] $RotationMetadataContainer = $null; hidden [string] $RotationMetadataContainerName = [Constants]::RotationMetadataContainerName CredHygiene([string] $subscriptionId, [InvocationInfo] $invocationContext): Base($subscriptionId, $invocationContext) { $this.DoNotOpenOutputFolder = $true; } hidden [void] GetAzSKRotationMetadatContainer() { if($null -eq $this.AzSKStorageAccount) { $this.GetAzSKStorageAccount() } if($null -eq $this.AzSKStorageAccount) { return; } try { $containerObject = Get-AzStorageContainer -Context $this.AzSKStorageAccount.Context -Name $this.RotationMetadataContainerName -ErrorAction Stop $this.RotationMetadataContainer = $containerObject; } catch { try { New-AzStorageContainer -Context $this.AzSKStorageAccount.Context -Name $this.RotationMetadataContainerName -ErrorAction SilentlyContinue $containerObject = Get-AzStorageContainer -Context $this.AzSKStorageAccount.Context -Name $this.RotationMetadataContainerName -ErrorAction SilentlyContinue $this.RotationMetadataContainer = $containerObject; } catch { #Do nothing } } } hidden [void] GetAzSKStorageAccount() { if($null -eq $this.AzSKResourceGroup) { $this.GetAzSKRG(); } if($null -ne $this.AzSKResourceGroup) { $StorageAccount = Get-AzStorageAccount -ResourceGroupName $this.AzSKResourceGroup.ResourceGroupName | Where-Object {$_.StorageAccountName -like 'azsk*'} -ErrorAction SilentlyContinue #if no storage account found then it assumes that there is no control state feature is not used and if there are more than one storage account found it assumes the same $this.AzSKStorageAccount = $StorageAccount; } } hidden [PSObject] GetAzSKRG() { $azSKConfigData = [ConfigurationManager]::GetAzSKConfigData() $resourceGroup = Get-AzResourceGroup -Name $azSKConfigData.AzSKRGName -ErrorAction SilentlyContinue $this.AzSKResourceGroup = $resourceGroup return $resourceGroup; } [void] PrintDetails($credentialInfo){ $this.PublishCustomMessage("`n") $this.PublishCustomMessage("Settings for the AzSK tracked credential [$($credentialInfo.credName)] `n`n", [MessageType]::Info) $this.PublishCustomMessage("`n") $this.PublishCustomMessage("Name:`t`t`t`t`t`t`t`t`t$($credentialInfo.credName)", [MessageType]::Default) $this.PublishCustomMessage("Location:`t`t`t`t`t`t`t`t$($credentialInfo.credLocation)", [MessageType]::Default) $this.PublishCustomMessage("Rotation interval (days):`t`t`t`t$($credentialInfo.rotationInt)", [MessageType]::Default) $this.PublishCustomMessage("Alert email:`t`t`t`t`t`t`t$($credentialInfo.emailId)", [MessageType]::Default) $this.PublishCustomMessage("Alert phone:`t`t`t`t`t`t`t$($credentialInfo.contactNumber)", [MessageType]::Default) $this.PublishCustomMessage("Created on:`t`t`t`t`t`t`t`t$($credentialInfo.firstUpdatedOn)", [MessageType]::Default) $this.PublishCustomMessage("Created by:`t`t`t`t`t`t`t`t$($credentialInfo.firstUpdatedBy)", [MessageType]::Default) $this.PublishCustomMessage("Last update:`t`t`t`t`t`t`t$($credentialInfo.lastUpdatedOn)", [MessageType]::Default) $this.PublishCustomMessage("Updated by:`t`t`t`t`t`t`t`t$($credentialInfo.lastUpdatedBy)", [MessageType]::Default) $this.PublishCustomMessage("Comment:`t`t`t`t`t`t`t`t$($credentialInfo.comment)`n", [MessageType]::Default) if($credentialInfo.credLocation -eq "AppService"){ $this.PublishCustomMessage([Constants]::SingleDashLine); $this.PublishCustomMessage("Credential Details:"); $this.PublishCustomMessage("AppService name:`t`t`t`t`t`t$($credentialInfo.resourceGroup)", [MessageType]::Default) $this.PublishCustomMessage("Resource group:`t`t`t`t`t`t`t$($credentialInfo.resourceName)", [MessageType]::Default) $this.PublishCustomMessage("AppService config type:`t`t`t`t`t$($credentialInfo.appConfigType)", [MessageType]::Default) $this.PublishCustomMessage("AppService config name:`t`t`t`t`t$($credentialInfo.appConfigName)", [MessageType]::Default) } if($credentialInfo.credLocation -eq "KeyVault"){ $this.PublishCustomMessage([Constants]::SingleDashLine); $this.PublishCustomMessage("Credential Details:"); $this.PublishCustomMessage("Key vault name:`t`t`t`t`t`t`t$($credentialInfo.kvName)", [MessageType]::Default) $this.PublishCustomMessage("Credential type:`t`t`t`t`t`t$($credentialInfo.kvCredType)", [MessageType]::Default) $this.PublishCustomMessage("Credential name:`t`t`t`t`t`t$($credentialInfo.kvCredName)", [MessageType]::Default) $this.PublishCustomMessage("Expiry time:`t`t`t`t`t`t`t$($credentialInfo.expiryTime)", [MessageType]::Default) $this.PublishCustomMessage("Version:`t`t`t`t`t`t`t`t$($credentialInfo.Version)", [MessageType]::Default) } $this.PublishCustomMessage("`n") } [void] PrintInfo ($credentialInfo){ $this.PublishCustomMessage("`n") $this.PublishCustomMessage("Settings for the AzSK tracked credential [$($credentialInfo.credName)] `n`n", [MessageType]::Info) $this.PublishCustomMessage("`n") $this.PublishCustomMessage("Name:`t`t`t`t`t`t`t`t`t$($credentialInfo.credName)", [MessageType]::Default) $this.PublishCustomMessage("Location:`t`t`t`t`t`t`t`t$($credentialInfo.credLocation)", [MessageType]::Default) $this.PublishCustomMessage("Rotation interval (days):`t`t`t`t$($credentialInfo.rotationInt)", [MessageType]::Default) $this.PublishCustomMessage("Alert email:`t`t`t`t`t`t`t$($credentialInfo.emailId)", [MessageType]::Default) $this.PublishCustomMessage("Alert phone:`t`t`t`t`t`t`t$($credentialInfo.contactNumber)", [MessageType]::Default) $this.PublishCustomMessage("Comment:`t`t`t`t`t`t`t`t$($credentialInfo.comment)`n", [MessageType]::Default) if($credentialInfo.credLocation -eq "AppService"){ $this.PublishCustomMessage([Constants]::SingleDashLine); $this.PublishCustomMessage("Credential Details:"); $this.PublishCustomMessage("AppService name:`t`t`t`t`t`t$($credentialInfo.resourceGroup)", [MessageType]::Default) $this.PublishCustomMessage("Resource group:`t`t`t`t`t`t`t$($credentialInfo.resourceName)", [MessageType]::Default) $this.PublishCustomMessage("AppService config type:`t`t`t`t`t$($credentialInfo.appConfigType)", [MessageType]::Default) $this.PublishCustomMessage("AppService config name:`t`t`t`t`t$($credentialInfo.appConfigName)", [MessageType]::Default) } if($credentialInfo.credLocation -eq "KeyVault"){ $this.PublishCustomMessage([Constants]::SingleDashLine); $this.PublishCustomMessage("Credential Details:"); $this.PublishCustomMessage("Key vault name:`t`t`t`t`t`t`t$($credentialInfo.kvName)", [MessageType]::Default) $this.PublishCustomMessage("Credential type:`t`t`t`t`t`t$($credentialInfo.kvCredType)", [MessageType]::Default) $this.PublishCustomMessage("Credential name:`t`t`t`t`t`t$($credentialInfo.kvCredName)", [MessageType]::Default) $this.PublishCustomMessage("Expiry time:`t`t`t`t`t`t`t$($credentialInfo.expiryTime)", [MessageType]::Default) $this.PublishCustomMessage("Version:`t`t`t`t`t`t`t`t$($credentialInfo.Version)", [MessageType]::Default) } $this.PublishCustomMessage("`n") } [void] GetAlert($CredentialName) { $file = Join-Path $($this.AzSKTemp) -ChildPath $($this.SubscriptionContext.SubscriptionId) | Join-Path -ChildPath $CredentialName $file += ".json" $this.GetAzSKRotationMetadatContainer() $tempSubPath = Join-Path $($this.AzSKTemp) $($this.SubscriptionContext.SubscriptionId) if(![string]::isnullorwhitespace($this.SubscriptionContext.SubscriptionId)){ if(-not (Test-Path $tempSubPath)) { New-Item -ItemType Directory -Path $tempSubPath -ErrorAction Stop | Out-Null } } else{ if(-not (Test-Path "$($this.AzSKTemp)")) { New-Item -ItemType Directory -Path "$($this.AzSKTemp)" -ErrorAction Stop | Out-Null } } if($CredentialName){ $blobName = $CredentialName.ToLower() + ".json" $blobContent = Get-AzStorageBlobContent -Blob $blobName -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -Destination $file -Force -ErrorAction Ignore if($blobContent){ $credentialInfo = Get-ChildItem -Path $file -Force | Get-Content | ConvertFrom-Json $this.PrintDetails($credentialInfo); } else{ $this.PublishCustomMessage("Could not find an entry for credential [$CredentialName].",[MessageType]::Critical) $this.PublishCustomMessage("Please check the name or run Get-AzSKTrackedCredential to list all credentials currently being tracked for rotation/expiry.") } } else{ $blob = @(); $blob = Get-AzStorageBlob -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -ErrorAction Ignore if($blob){ $this.PublishCustomMessage("`n") $this.PublishCustomMessage("`nListing settings for all the credentials `n`n",[MessageType]::Update) $this.PublishCustomMessage("`n") $blob | where { $_ | Get-AzStorageBlobContent -Destination $file -Force | Out-Null $credentialInfo = Get-ChildItem -Path $file -Force | Get-Content | ConvertFrom-Json $this.PrintDetails($credentialInfo); } } else{ $this.PublishCustomMessage("No credential entries found for rotation/expiry alert.", [MessageType]::Critical) } } if(Test-Path $file) { Remove-Item -Path $file } } [void] NewAlert($CredentialLocation, $ResourceGroupName, $ResourceName, $AppConfigType, $AppConfigName, $KVName, $KVCredentialType, $KVCredentialName) { $file = Join-Path $($this.AzSKTemp) -ChildPath $($this.SubscriptionContext.SubscriptionId) | Join-Path -ChildPath $($this.credName) $file += ".json" $this.GetAzSKRotationMetadatContainer() $blobName = ($this.credName).ToLower() + ".json" [bool] $found = $true; $tempSubPath = Join-Path $($this.AzSKTemp) $($this.SubscriptionContext.SubscriptionId) if(![string]::isnullorwhitespace($this.SubscriptionContext.SubscriptionId)){ if(-not (Test-Path $tempSubPath)) { New-Item -ItemType Directory -Path $tempSubPath -ErrorAction Stop | Out-Null } } else{ if(-not (Test-Path "$($this.AzSKTemp)")) { New-Item -ItemType Directory -Path "$($this.AzSKTemp)" -ErrorAction Stop | Out-Null } } $blobContent = Get-AzStorageBlobContent -Blob $blobName -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -Destination $file -Force -ErrorAction Ignore if($blobContent){ $this.PublishCustomMessage("Entry for the credential [$($this.credName)] already exists.", [MessageType]::Error); $this.PublishCustomMessage("Run Update-AzSKTrackedCredential to update alert configurations for the existing credential.", [MessageType]::Default) } else{ $startTime = [DateTime]::UtcNow $user = ([ContextHelper]::GetCurrentRMContext()).Account.Id $this.PublishCustomMessage("Onboarding the credential [$($this.credName)] for rotation/expiry notification", [MessageType]::Default) $credentialInfo = New-Object PSObject Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name credLocation -Value $this.credLocation Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name credName -Value $this.credName Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name rotationInt -Value $this.rotationInt Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name emailId -Value $this.alertEmail Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name contactNumber -Value $this.alertPhoneNumber Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name firstUpdatedOn -Value $startTime Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name lastUpdatedOn -Value $startTime Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name comment -Value $this.comment Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name firstUpdatedBy -Value $user Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name lastUpdatedBy -Value $user if($CredentialLocation -eq "AppService"){ Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name resourceGroup -Value $ResourceGroupName Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name resourceName -Value $ResourceName Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name appConfigType -Value $AppConfigType Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name appConfigName -Value $AppConfigName $resource = Get-AzWebApp -ResourceGroupName $credentialInfo.resourceGroup -Name $credentialInfo.resourceName -ErrorAction Ignore if(-not $resource){ $found = $false; $this.PublishCustomMessage("Could not find app service [$ResourceName] and/or resource group [$ResourceGroupName]. Please check the names.",[MessageType]::Error) } else{ if($AppConfigType -eq "Application Settings") { if(-not ($resource.SiteConfig.AppSettings.Name | where-object{$_ -eq $AppConfigName})){ $found = $false; $this.PublishCustomMessage("Could not find app setting [$AppConfigName] in the app service [$ResourceName]. Please check the name.",[MessageType]::Error) } } elseif($AppConfigType -eq "Connection Strings") { if(-not ($resource.SiteConfig.ConnectionStrings.Name | where-object{$_ -eq $AppConfigName})){ $found = $false; $this.PublishCustomMessage("Could not find connection string [$AppConfigName] in the app service [$ResourceName]. Please check the name.",[MessageType]::Error) } } } } if($CredentialLocation -eq "KeyVault"){ Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name kvName -Value $KVName Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name kvCredType -Value $KVCredentialType Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name kvCredName -Value $KVCredentialName if($KVCredentialType -eq "Key") { $key = Get-AzKeyVaultKey -VaultName $KVName -Name $KVCredentialName -ErrorAction Ignore if($key){ Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name expiryTime -Value $key.Expires Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name version -Value $key.Version } else{ $found = $false; $this.PublishCustomMessage("Could not find key [$KVCredentialName] in key vault [$KVName]. Please check the names.",[MessageType]::Error) } } elseif($KVCredentialType -eq "Secret") { $secret = Get-AzKeyVaultSecret -VaultName $KVName -Name $KVCredentialName -ErrorAction Ignore if($secret){ Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name expiryTime -Value $secret.Expires Add-Member -InputObject $credentialInfo -MemberType NoteProperty -Name version -Value $secret.Version } else{ $found = $false; $this.PublishCustomMessage("Could not find secret [$KVCredentialName] in key vault [$KVName]. Please check the names.",[MessageType]::Error) } } } if($found){ $credentialInfo | ConvertTo-Json -Depth 10 | Out-File $file -Force Set-AzStorageBlobContent -Blob $blobName -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -File $file -Force | Out-Null $this.PublishCustomMessage("Successfully onboarded the credential [$($this.credName)] for rotation/expiry notification", [MessageType]::Update) $this.PrintInfo($credentialInfo); } else{ $this.PublishCustomMessage("Could not onboard the credential [$($this.credName)] for rotation/expiry notification", [MessageType]::Error) } } if(Test-Path $file) { Remove-Item -Path $file } $this.PublishCustomMessage("Run Get-AzSKTrackedCredential to list all the credentials onboarded for rotation/expiry notification.") } [void] RemoveAlert($CredentialName,$Force) { $file = Join-Path $($this.AzSKTemp) -ChildPath $($this.SubscriptionContext.SubscriptionId) | Join-Path -ChildPath $CredentialName $file += ".json" $this.GetAzSKRotationMetadatContainer() $blobName = $CredentialName.ToLower() + ".json" $tempSubPath = Join-Path $($this.AzSKTemp) $($this.SubscriptionContext.SubscriptionId) if(![string]::isnullorwhitespace($this.SubscriptionContext.SubscriptionId)){ if(-not (Test-Path $tempSubPath)) { New-Item -ItemType Directory -Path $tempSubPath -ErrorAction Stop | Out-Null } } else{ if(-not (Test-Path "$($this.AzSKTemp)")) { New-Item -ItemType Directory -Path "$($this.AzSKTemp)" -ErrorAction Stop | Out-Null } } $blobContent = Get-AzStorageBlobContent -Blob $blobName -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -Destination $file -Force -ErrorAction Ignore if($blobContent){ $title = "Confirm the removal of credential rotation/expiry notification" $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "This means Yes" $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "This means No" $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) #below is hack for removing error due to strict mode - host variable is not assigned in the method $host = $host # Ask for confirmation only if force switch is not present $result = 0 if(!$Force) { #user confirmation before deleting container $storageConfirmMsg = "Are you sure you want to remove expiry alerting on [$CredentialName] credential?" $result = $host.ui.PromptForChoice($title, $storageConfirmMsg, $options, 1) } if($result -eq 0) { #user selected yes $this.PublishCustomMessage("Removing the rotation/expiry notification on the credential [$CredentialName]", [MessageType]::Default) $blobContent | Remove-AzStorageBlob $this.PublishCustomMessage("Successfully removed the rotation/expiry notification on the credential [$CredentialName]", [MessageType]::Update) } #user selected no in confirmation box else { $this.PublishCustomMessage("You have chosen not to remove rotation/expiry notification on the credential [$CredentialName]", [MessageType]::Default) } } else{ $this.PublishCustomMessage("Could not find an entry for credential [$CredentialName].", [MessageType]::Critical) } if(Test-Path $file) { Remove-Item -Path $file } } [void] UpdateAlert($CredentialName,$RotationIntervalInDays,$AlertEmail,$AlertSMS,$Comment,$UpdateCredential,$ResetLastUpdate) { $file = Join-Path $($this.AzSKTemp) -ChildPath $($this.SubscriptionContext.SubscriptionId) | Join-Path -ChildPath $CredentialName $file += ".json" $this.GetAzSKRotationMetadatContainer() $blobName = $CredentialName.ToLower() + ".json" $tempSubPath = Join-Path $($this.AzSKTemp) $($this.SubscriptionContext.SubscriptionId) if(![string]::isnullorwhitespace($this.SubscriptionContext.SubscriptionId)){ if(-not (Test-Path $tempSubPath)) { New-Item -ItemType Directory -Path $tempSubPath -ErrorAction Stop | Out-Null } } else{ if(-not (Test-Path "$($this.AzSKTemp)")) { New-Item -ItemType Directory -Path "$($this.AzSKTemp)" -ErrorAction Stop | Out-Null } } $blobContent = Get-AzStorageBlobContent -Blob $blobName -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -Destination $file -Force -ErrorAction Ignore if($blobContent){ $this.PublishCustomMessage("Updating settings for AzSK tracked credential [$CredentialName]", [MessageType]::Default) $credentialInfo = Get-ChildItem -Path $file -Force | Get-Content | ConvertFrom-Json $user = ([ContextHelper]::GetCurrentRMContext()).Account.Id; if ($RotationIntervalInDays) { $credentialInfo.rotationInt = $RotationIntervalInDays; } if ($AlertEmail) { $credentialInfo.emailId = $AlertEmail; } if ($AlertSMS) { $credentialInfo.contactNumber = $AlertSMS; } if($UpdateCredential){ if($credentialInfo.credLocation -eq "AppService"){ $resource = Get-AzWebApp -ResourceGroupName $credentialInfo.resourceGroup -Name $credentialInfo.resourceName $hash = @{} if($credentialInfo.appConfigType -eq "Application Settings"){ $this.PublishCustomMessage("Updating the application setting [$($credentialInfo.appConfigName)] in the app service [$($credentialInfo.resourceName)]", [MessageType]::Default) $appConfig = $resource.SiteConfig.AppSettings $appConfigValue = Read-Host "Enter the new secret for the application setting - [$($credentialInfo.appConfigName)]" -AsSecureString $appConfigValue = [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($appConfigValue)) foreach ($setting in $appConfig) { if($setting.Name -eq $credentialInfo.appConfigName){ $hash[$setting.Name] = $appConfigValue } else{ $hash[$setting.Name] = $setting.Value } } } elseif($credentialInfo.appConfigType -eq "Connection Strings"){ $this.PublishCustomMessage("Updating the connection string [$($credentialInfo.appConfigName)] in the app service [$($credentialInfo.resourceName)]", [MessageType]::Default) $appConfig = $resource.SiteConfig.ConnectionStrings $appConfigValue = Read-Host "Enter the new secret for the connection string - [$($credentialInfo.appConfigName)]" -AsSecureString $appConfigValue = [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($appConfigValue)) foreach ($setting in $appConfig) { if($setting.Name -eq $credentialInfo.appConfigName){ $hash[$setting.Name] = @{Type=$setting.Type.ToString(); Value=$appConfigValue} } else{ $hash[$setting.Name] = @{Type=$setting.Type.ToString(); Value=$setting.ConnectionString} } } } if($credentialInfo.appConfigType -eq "Application Settings"){ Set-AzWebApp -ResourceGroupName $credentialInfo.resourceGroup -Name $credentialInfo.resourceName -AppSettings $hash | Out-Null $this.PublishCustomMessage("Successfully updated the application setting [$($credentialInfo.appConfigName)] in the app service [$($credentialInfo.resourceName)]", [MessageType]::Update) } elseif($credentialInfo.appConfigType -eq "Connection Strings"){ Set-AzWebApp -ResourceGroupName $credentialInfo.resourceGroup -Name $credentialInfo.resourceName -ConnectionStrings $hash | Out-Null $this.PublishCustomMessage("Successfully updated connection string [$($credentialInfo.appConfigName)] in the app service [$($credentialInfo.resourceName)]", [MessageType]::Update) } } if($credentialInfo.credLocation -eq "KeyVault") { if($credentialInfo.kvCredType -eq "Key") { $currentKey = Get-AzKeyVaultKey -VaultName $credentialInfo.kvName -Name $credentialInfo.kvCredName -ErrorAction SilentlyContinue if(($currentKey | Measure-Object).Count -ne 0) { $currentTime = [DateTime]::UtcNow $expiryTime = $currentTime.AddDays($credentialInfo.rotationInt) $this.PublishCustomMessage("Updating the key [$($credentialInfo.kvCredName)] in the key vault [$($credentialInfo.kvName)]", [MessageType]::Default) $newKey = Add-AzKeyVaultKey -VaultName $credentialInfo.kvName -Name $credentialInfo.kvCredName -Expires $ExpiryTime -Destination Software $credentialInfo.expiryTime = $newKey.Expires $credentialInfo.version = $newKey.Version $this.PublishCustomMessage("Successfully updated the key [$($credentialInfo.kvCredName)] in the key vault [$($credentialInfo.kvName)]", [MessageType]::Update) } } elseif($credentialInfo.kvCredType -eq "Secret") { $currentSecret = Get-AzKeyVaultSecret -VaultName $credentialInfo.kvName -Name $credentialInfo.kvCredName -ErrorAction SilentlyContinue if(($currentSecret | Measure-Object).Count -ne 0) { $currentTime = [DateTime]::UtcNow $expiryTime = $currentTime.AddDays($credentialInfo.rotationInt) $this.PublishCustomMessage("Updating the secret [$($credentialInfo.kvCredName)] in the key vault [$($credentialInfo.kvName)]", [MessageType]::Default) $secret = Read-Host "Enter the new secret value for the key vault credential - [$($credentialInfo.kvCredName)]" -AsSecureString $newSecret = Set-AzKeyVaultSecret -VaultName $credentialInfo.kvName -Name $credentialInfo.kvCredName -SecretValue $secret -Expires $ExpiryTime $credentialInfo.expiryTime = $newSecret.Expires $credentialInfo.version = $newSecret.Version $this.PublishCustomMessage("Successfully updated the secret [$($credentialInfo.kvCredName)] in the key vault [$($credentialInfo.kvName)]", [MessageType]::Update) } } $this.PublishCustomMessage("To avoid impacting availability, the previous version of this $($credentialInfo.kvCredType) has been kept in enabled state.", [MessageType]::Warning) $this.PublishCustomMessage("If you are using key/secret URLs with specific version identifier in them, please update to the new version before disabling it.") } $credentialInfo.lastUpdatedOn = [DateTime]::UtcNow $credentialInfo.lastUpdatedBy = $user } else{ if($ResetLastUpdate){ $credentialInfo.lastUpdatedOn = [DateTime]::UtcNow $credentialInfo.lastUpdatedBy = $user } } $credentialInfo.comment = $Comment $credentialInfo | ConvertTo-Json -Depth 10 | Out-File $file -Force Set-AzStorageBlobContent -Blob $blobName -Container $this.RotationMetadataContainerName -Context $this.AzSKStorageAccount.Context -File $file -Force | Out-Null $this.PublishCustomMessage("Successfully updated settings for AzSK tracked credential [$CredentialName]", [MessageType]::Update) $this.PrintInfo($credentialInfo); } else{ $this.PublishCustomMessage("Could not find an entry for credential [$CredentialName].", [MessageType]::Critical) } if(Test-Path $file) { Remove-Item -Path $file } } [void] InstallAlert($AlertEmail) { $rgName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName $credHygieneAGName = [Constants]::CredHygieneActionGroupName $credHygieneAGShortName = [Constants]::CredHygieneActionGroupShortName $email = New-AzActionGroupReceiver -Name $AlertEmail -EmailReceiver -EmailAddress $AlertEmail $actionGroup = Set-AzActionGroup -Name $credHygieneAGName -ResourceGroupName $rgName -ShortName $credHygieneAGShortName -Receiver $email -ErrorAction Ignore -WarningAction SilentlyContinue if($actionGroup){ # We are using LAWS from the same sub for Alert REST API call. $automationAccDetails= Get-AzAutomationAccount -ResourceGroupName $rgName -ErrorAction SilentlyContinue if($automationAccDetails) { #Fetch LAWS Id from CA variables $laWSId = Get-AzAutomationVariable -ResourceGroupName $automationAccDetails.ResourceGroupName -AutomationAccountName $automationAccDetails.AutomationAccountName -Name "LAWSId" -ErrorAction SilentlyContinue if($laWSId){ $laWS = Get-AzOperationalInsightsWorkspace | where{$_.CustomerId -eq $laWSId.Value} # Verify whether the LA resource ith the WS id exists. if($laWS){ $body = [ConfigurationManager]::LoadServerConfigFile("CredentialHygieneAlert.json"); $dataSourceId = $body.properties.source.dataSourceId | ConvertTo-Json -Depth 10 $dataSourceId = $dataSourceId.Replace("{0}",$this.SubscriptionContext.SubscriptionId).Replace("{1}",$laWS.ResourceGroupName).Replace("{2}",$laWS.CustomerId) | ConvertFrom-Json $body.properties.source.dataSourceId = $dataSourceId $ag = $body.properties.action.aznsAction.actionGroup[0] | ConvertTo-Json -Depth 10 $ag = $ag.Replace("{3}",$actionGroup.Id) | ConvertFrom-Json $body.properties.action.aznsAction.actionGroup[0] = $ag $ResourceAppIdURI = [WebRequestHelper]::GetResourceManagerUrl() $uri = $ResourceAppIdURI + "subscriptions/$($this.SubscriptionContext.SubscriptionId)/resourcegroups/$($laWS.ResourceGroupName)/providers/microsoft.insights/scheduledQueryRules/credHygieneQueryRule?api-version=2018-04-16" [WebRequestHelper]::InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod]::Put, $uri, $body); } else{ # LA resource not found. $this.PublishCustomMessage("Log analytics resource with workspace id [$($laWSId.Value)] provided in the CA automation account variables doesn't exist. Please verify the value of log analytics workspace id.", [MessageType]::Error) $this.PublishCustomMessage("Couldn't create credential hygiene alert for the current subscription.", [MessageType]::Error) $this.PublishCustomMessage("Run Update-AzSKContinuousAssurance to update the log analytics workspace id with the correct value in the CA automation account.") } } else{ # LAWS id variable not found. $this.PublishCustomMessage("Log analytics workspace id not found in the CA automation account variables.", [MessageType]::Error) $this.PublishCustomMessage("Couldn't create credential hygiene alert for the current subscription.", [MessageType]::Error) $this.PublishCustomMessage("Run Update-AzSKContinuousAssurance to update the log analytics workspace id with the correct value in the CA automation account.") } } else{ # CA setup not found. $this.PublishCustomMessage("Continuous Assurance setup was not found in the current subscription.", [MessageType]::Error) $this.PublishCustomMessage("Couldn't create credential hygiene alert for the current subscription.", [MessageType]::Error) } } else{ # Action group not found/created. $this.PublishCustomMessage("Couldn't create action group [$credHygieneAGName] for credential hygiene alerts.", [MessageType]::Error) $this.PublishCustomMessage("Couldn't create credential hygiene alert for the current subscription.", [MessageType]::Error) } } } |