ps/Modules/Alkami.DevOps.SystemEngineering/Public/New-ServerlessServiceAccountPair.tests.ps1
2023-05-30 22:51:22 -07:00

292 lines
20 KiB
PowerShell

. $PSScriptRoot\..\..\Load-PesterModules.ps1
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.tests\.', '.'
$global:functionPath = Join-Path -Path $here -ChildPath $sut
InModuleScope -ModuleName Alkami.DevOps.SystemEngineering -ScriptBlock {
Write-Host "InModuleScope - Overriding SUT: $global:functionPath"
Import-Module $global:functionPath -Force
$inScopeModule = "Alkami.DevOps.SystemEngineering"
Describe "New-ServerlessServiceAccountPair" {
Mock -CommandName Get-LogLeadName -ModuleName $inScopeModule -MockWith { return 'New-ServerlessServiceAccountPair.tests' }
Mock -CommandName Get-AWSRegion -ModuleName $inScopeModule -MockWith { return @( @{ 'Region' = 'us-east-1' }, @{ 'Region' = 'us-west-2' } ) }
Mock -CommandName New-SecurePassword -ModuleName $inScopeModule -MockWith { return 'TestPassword' }
Mock -CommandName New-ServerlessServiceAccountActiveDirectoryUserPair -ModuleName $inScopeModule -MockWith {}
Mock -CommandName New-ServerlessServiceAccountSecret -ModuleName $inScopeModule -MockWith { return @('TestSecretArn1', 'TestSecretArn2') }
Mock -CommandName New-ServerlessServiceAccountIamPolicy -ModuleName $inScopeModule -MockWith {}
Mock -CommandName Get-YeatsLambdaIamRoleArn -ModuleName $inScopeModule -MockWith { return 'TestYeatsRoleArn' }
Mock -CommandName Get-YeatsRotationLambdaArn -ModuleName $inScopeModule -MockWith { return 'TestYeatsLambdaArn' }
Mock -CommandName Write-AlkamiSecretResourcePolicy -ModuleName $inScopeModule -MockWith {}
Mock -CommandName Invoke-SECSecretRotation -ModuleName $inScopeModule -MockWith {}
Mock -CommandName Write-Error -ModuleName $inScopeModule -MockWith {}
Mock -CommandName Write-Warning -ModuleName $inScopeModule -MockWith {}
Mock -CommandName Get-AlkamiCredential -ModuleName $inScopeModule -MockWith {
return ( New-Object 'Management.Automation.PsCredential' $UserName, ( ConvertTo-SecureString -AsPlainText -Force -String "$Password" ))
}
$testCredential = New-Object 'Management.Automation.PsCredential' 'Test', ( ConvertTo-SecureString -AsPlainText -Force -String 'Test' )
$validEnvironment = (Get-Command New-ServerlessServiceAccountPair).Parameters.Environment.Attributes.ValidValues[0]
Context "Parameter Validation" {
It "Throws if Service Name Length Is Too Short" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName '' } | Should -Throw
}
It "Throws if Service Name Length Is Too Long" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'ThisIsAServiceThatWeShouldDeployEverywhere' } | Should -Throw
}
It "Throws if Environment is Not in Supported List" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment 'Test' } | Should -Throw
}
It "Throws if Environment is in Supported List With Different Casing" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validValue.ToLower() } | Should -Throw
}
It "Throws if Profile Name is Null" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName $null } | Should -Throw
}
It "Throws if Profile Name is Empty" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName '' } | Should -Throw
}
It "Throws if Ticket Number Does Not Match Regex" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test!' } | Should -Throw
}
It "Throws if Role ARN is Null" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn $null } | Should -Throw
}
It "Throws if Role ARN is Empty" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn '' } | Should -Throw
}
It "Throws if Region is Not in Supported List" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'Test' } | Should -Throw
}
It "Throws if Replication Region is Not in Supported List" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' -ReplicationRegion 'Test' } | Should -Throw
}
It "Throws if Rotation Window is Null" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' -RotationWindow $null } | Should -Throw
}
It "Throws if Rotation Window is Empty" {
{ New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' -RotationWindow '' } | Should -Throw
}
}
Context "Logic" {
It "Creates Two User Passwords" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-SecurePassword -Times 2 -Exactly -Scope It
}
It "Proxies Provided Arguments to Active Directory Function" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountActiveDirectoryUserPair -Times 1 -Exactly -Scope It `
-ParameterFilter { (($Cred.UserName -ceq 'Test') -and ($Environment -ceq $validEnvironment) -and `
($TicketNumber -ceq 'Test-123')) }
}
It "Aborts Processing if Active Directory Function Throws" {
Mock -CommandName New-ServerlessServiceAccountActiveDirectoryUserPair -ModuleName $inScopeModule -MockWith { throw "Test" }
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountActiveDirectoryUserPair -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountSecret -Times 0 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-Error -Times 1 -Exactly -Scope It `
-ParameterFilter { $Message -match 'Creation of Active Directory users failed. Error encountered was' }
# Revert to top-level mock.
Mock -CommandName New-ServerlessServiceAccountActiveDirectoryUserPair -ModuleName $inScopeModule -MockWith {}
}
It "Proxies Provided Arguments to AWS Secret Function" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' -ReplicationRegion 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountSecret -Times 1 -Exactly -Scope It `
-ParameterFilter { (($ProfileName -ceq 'temp-test') -and ($Region -ceq 'us-east-1') -and `
($ReplicationRegion -ceq 'us-east-1')) }
}
It "Aborts Processing if Secret Creation Function Throws" {
Mock -CommandName New-ServerlessServiceAccountSecret -ModuleName $inScopeModule -MockWith { throw "Test" }
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountSecret -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountIamPolicy -Times 0 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-Error -Times 1 -Exactly -Scope It `
-ParameterFilter { $Message -match 'Creation of AWS secret failed. Error encountered was' }
# Revert mock back to top-level.
Mock -CommandName New-ServerlessServiceAccountSecret -ModuleName $inScopeModule -MockWith { return @('TestSecretArn1', 'TestSecretArn2') }
}
It "Proxies Provided Arguments to AWS IAM Policy Function" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountIamPolicy -Times 1 -Exactly -Scope It `
-ParameterFilter { (($ProfileName -ceq 'temp-test') -and ($Region -ceq 'us-east-1') -and ($RoleArn -ceq 'TestRoleArn')) }
}
It "Continues Processing With Warning if AWS IAM Policy Creation Function Throws" {
Mock -CommandName New-ServerlessServiceAccountIamPolicy -ModuleName $inScopeModule -MockWith { throw "Test" }
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName New-ServerlessServiceAccountIamPolicy -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Get-YeatsLambdaIamRoleArn -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-Warning -Times 1 -Exactly -Scope It `
-ParameterFilter { $Message -match 'Creation of IAM policy failed. Error encountered was' }
# Revert mock back to top-level.
Mock -CommandName New-ServerlessServiceAccountIamPolicy -ModuleName $inScopeModule -MockWith {}
}
It "Grants Supplied IAM Role Access to Created AWS Secret in Resource Policy" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-AlkamiSecretResourcePolicy -Times 1 -Exactly -Scope It `
-ParameterFilter { $SecretAccessExtraArns -contains 'TestRoleArn' }
}
It "Grants Yeats Lambda IAM Role Access to Created AWS Secret in Resource Policy" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Get-YeatsLambdaIamRoleArn -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-AlkamiSecretResourcePolicy -Times 1 -Exactly -Scope It `
-ParameterFilter { $SecretAccessExtraArns -contains 'TestYeatsRoleArn' }
}
It "Grants Provided Extra ARNs Access to Created AWS Secret in Resource Policy" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' -SecretAccessExtraArns @('TestExtraArn') | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-AlkamiSecretResourcePolicy -Times 1 -Exactly -Scope It `
-ParameterFilter { $SecretAccessExtraArns -contains 'TestExtraArn' }
}
It "Skips Secret Rotation Configuration if User Explicitly Disabled Feature" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' -RotationSchedule $null | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Get-YeatsRotationLambdaArn -Times 0 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Invoke-SECSecretRotation -Times 0 -Exactly -Scope It
}
It "Skips Secret Rotation Configuration if Yeats Rotation Lambda Is Not Found" {
Mock -CommandName Get-YeatsRotationLambdaArn -ModuleName $inScopeModule -MockWith { return $null }
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Get-YeatsRotationLambdaArn -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Invoke-SECSecretRotation -Times 0 -Exactly -Scope It
# Revert mock back to top-level.
Mock -CommandName Get-YeatsRotationLambdaArn -ModuleName $inScopeModule -MockWith { return 'TestYeatsLambdaArn' }
}
It "Applies Secret Rotation Configuration if Yeats Rotation Lambda Is Found" {
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Get-YeatsRotationLambdaArn -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Invoke-SECSecretRotation -Times 1 -Exactly -Scope It `
-ParameterFilter { $RotationLambdaArn -eq 'TestYeatsLambdaArn' }
}
It "Writes Warning if Configuration of AWS Secret Throws" {
Mock -CommandName Write-AlkamiSecretResourcePolicy -ModuleName $inScopeModule -MockWith { throw "Test" }
New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1' | Out-Null
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-AlkamiSecretResourcePolicy -Times 1 -Exactly -Scope It
Assert-MockCalled -ModuleName $inScopeModule -CommandName Write-Warning -Times 1 -Exactly -Scope It `
-ParameterFilter { $Message -match 'Configuration of AWS secret failed. Error encountered was' }
# Revert mock back to top-level.
Mock -CommandName Write-AlkamiSecretResourcePolicy -ModuleName $inScopeModule -MockWith {}
}
}
Context "Output" {
It "Returns a PSObject" {
(Get-Command New-ServerlessServiceAccountPair).OutputType.Type.ToString() | Should -BeExactly "System.Management.Automation.PSObject"
}
It "Returned Object Contains Usernames and Passwords for Created Users" {
$result = New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1'
$result.Credentials | Should -HaveCount 2
$result.Credentials[0].UserName | Should -BeExactly "${validEnvironment}TestSvcSvcA"
( Get-PasswordFromCredential $result.Credentials[0] ) | Should -BeExactly "TestPassword"
$result.Credentials[1].UserName | Should -BeExactly "${validEnvironment}TestSvcSvcB"
( Get-PasswordFromCredential $result.Credentials[1] ) | Should -BeExactly "TestPassword"
}
It "Returned Object Contains Secret ARNs" {
$result = New-ServerlessServiceAccountPair -Cred $testCredential -ServiceName 'TestSvc' -Environment $validEnvironment -ProfileName 'temp-test' `
-TicketNumber 'Test-123' -ServiceAccountIamRoleArn 'TestRoleArn' -Region 'us-east-1'
$result.SecretArns | Should -HaveCount 2
$result.SecretArns[0] | Should -BeExactly "TestSecretArn1"
$result.SecretArns[1] | Should -BeExactly "TestSecretArn2"
}
}
}
}