modules/Azure/Discovery/Tests/Unit/CIEMAzureArmResource.Tests.ps1
|
BeforeAll { Remove-Module Devolutions.CIEM -Force -ErrorAction SilentlyContinue Import-Module (Join-Path $PSScriptRoot '..' '..' '..' '..' '..' 'Devolutions.CIEM.psd1') # Create isolated test DB with base + azure + discovery schemas New-CIEMDatabase -Path "$TestDrive/ciem.db" InModuleScope Devolutions.CIEM { $script:DatabasePath = "$TestDrive/ciem.db" } foreach ($schemaPath in @( (Join-Path $PSScriptRoot '..' '..' '..' 'Infrastructure' 'Data' 'azure_schema.sql'), (Join-Path $PSScriptRoot '..' '..' 'Data' 'discovery_schema.sql') )) { foreach ($statement in ((Get-Content $schemaPath -Raw) -split ';\s*\n' | Where-Object { $_.Trim() })) { $trimmed = $statement.Trim() try { Invoke-CIEMQuery -Query $trimmed -AsNonQuery | Out-Null } catch { if ($trimmed -match 'ALTER\s+TABLE' -and $_.Exception.Message -match 'duplicate column') { continue } throw } } } } Describe 'ARM Resource CRUD' { Context 'New-CIEMAzureArmResource' { BeforeEach { Invoke-CIEMQuery -Query "DELETE FROM azure_arm_resources" } It 'Creates a resource and returns CIEMAzureArmResource object' { $result = New-CIEMAzureArmResource -Id '/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm1' ` -Type 'microsoft.compute/virtualmachines' ` -Name 'vm1' ` -Location 'eastus' ` -ResourceGroup 'rg1' ` -SubscriptionId 'sub1' ` -TenantId 'tenant1' $result | Should -Not -BeNullOrEmpty $result.GetType().Name | Should -Be 'CIEMAzureArmResource' $result.Id | Should -Be '/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm1' $result.Name | Should -Be 'vm1' } It 'Throws when resource with same Id already exists' { New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-dup' -Type 'microsoft.compute/virtualmachines' -Name 'vm-dup' { New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-dup' -Type 'microsoft.compute/virtualmachines' -Name 'vm-dup' } | Should -Throw } It 'Accepts -InputObject parameter set' { $obj = InModuleScope Devolutions.CIEM { $o = [CIEMAzureArmResource]::new() $o.Id = '/subscriptions/sub1/rg/vm-input' $o.Type = 'microsoft.compute/virtualmachines' $o.Name = 'vm-input' $o } $result = New-CIEMAzureArmResource -InputObject $obj $result | Should -Not -BeNullOrEmpty $result.Id | Should -Be '/subscriptions/sub1/rg/vm-input' } It 'Sets CollectedAt to current time when not provided' { $before = (Get-Date).ToString('o') $result = New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-time' -Type 'microsoft.compute/virtualmachines' -Name 'vm-time' $result.CollectedAt | Should -Not -BeNullOrEmpty $result.CollectedAt | Should -BeGreaterOrEqual $before } } Context 'Get-CIEMAzureArmResource' { BeforeAll { Invoke-CIEMQuery -Query "DELETE FROM azure_arm_resources" # Seed 3 test resources New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg1/vm/get1' -Type 'microsoft.compute/virtualmachines' -Name 'get-vm1' -Location 'eastus' -ResourceGroup 'rg1' -SubscriptionId 'sub1' New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg1/nsg/get2' -Type 'microsoft.network/networksecuritygroups' -Name 'get-nsg1' -Location 'westus' -ResourceGroup 'rg1' -SubscriptionId 'sub1' New-CIEMAzureArmResource -Id '/subscriptions/sub2/rg2/vm/get3' -Type 'microsoft.compute/virtualmachines' -Name 'get-vm2' -Location 'eastus' -ResourceGroup 'rg2' -SubscriptionId 'sub2' } It 'Returns all resources when no filter' { $results = Get-CIEMAzureArmResource $results | Should -HaveCount 3 } It 'Returns CIEMAzureArmResource typed objects (.GetType().Name -eq CIEMAzureArmResource)' { $results = Get-CIEMAzureArmResource $results | ForEach-Object { $_.GetType().Name | Should -Be 'CIEMAzureArmResource' } } It 'Filters by -Id' { $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg1/vm/get1' $result | Should -Not -BeNullOrEmpty $result.Name | Should -Be 'get-vm1' } It 'Filters by -Type' { $results = Get-CIEMAzureArmResource -Type 'microsoft.compute/virtualmachines' $results | Should -HaveCount 2 } It 'Filters by -Name' { $result = Get-CIEMAzureArmResource -Name 'get-nsg1' $result | Should -Not -BeNullOrEmpty $result.Type | Should -Be 'microsoft.network/networksecuritygroups' } It 'Filters by -SubscriptionId' { $results = Get-CIEMAzureArmResource -SubscriptionId 'sub2' $results | Should -HaveCount 1 $results[0].Name | Should -Be 'get-vm2' } It 'Filters by -ResourceGroup' { $results = Get-CIEMAzureArmResource -ResourceGroup 'rg1' $results | Should -HaveCount 2 } It 'Returns empty array when no match' { $results = Get-CIEMAzureArmResource -Id '/nonexistent' $results | Should -BeNullOrEmpty } } Context 'Update-CIEMAzureArmResource' { BeforeEach { Invoke-CIEMQuery -Query "DELETE FROM azure_arm_resources" New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' -Type 'microsoft.compute/virtualmachines' -Name 'vm-update' -Location 'eastus' -Properties '{"vmSize":"Standard_B1s"}' } It 'Updates Properties field via -Properties parameter' { Update-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' -Properties '{"vmSize":"Standard_D2s_v3"}' $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' $result.Properties | Should -Be '{"vmSize":"Standard_D2s_v3"}' } It 'Does not overwrite unspecified fields (partial update)' { Update-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' -Properties '{"updated":true}' $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' $result.Location | Should -Be 'eastus' $result.Name | Should -Be 'vm-update' } It 'Returns nothing without -PassThru' { $result = Update-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' -Properties '{"x":1}' $result | Should -BeNullOrEmpty } It 'Returns updated object with -PassThru' { $result = Update-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' -Properties '{"passthru":true}' -PassThru $result | Should -Not -BeNullOrEmpty $result.GetType().Name | Should -Be 'CIEMAzureArmResource' $result.Properties | Should -Be '{"passthru":true}' } It 'Accepts -InputObject for full object update' { $obj = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' $obj.Location = 'westus2' $obj.Tags = '{"env":"test"}' Update-CIEMAzureArmResource -InputObject $obj $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-upd' $result.Location | Should -Be 'westus2' $result.Tags | Should -Be '{"env":"test"}' } } Context 'Save-CIEMAzureArmResource' { BeforeEach { Invoke-CIEMQuery -Query "DELETE FROM azure_arm_resources" } It 'Inserts a new resource (upsert)' { Save-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-save-new' -Type 'microsoft.compute/virtualmachines' -Name 'vm-save-new' $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-save-new' $result | Should -Not -BeNullOrEmpty $result.Name | Should -Be 'vm-save-new' } It 'Updates existing resource with same Id (upsert)' { Save-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-save-up' -Type 'microsoft.compute/virtualmachines' -Name 'vm-original' Save-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-save-up' -Type 'microsoft.compute/virtualmachines' -Name 'vm-updated' $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-save-up' $result.Name | Should -Be 'vm-updated' # Only 1 row, not 2 $all = Get-CIEMAzureArmResource $all | Should -HaveCount 1 } It 'Accepts -InputObject via pipeline' { $obj = InModuleScope Devolutions.CIEM { $o = [CIEMAzureArmResource]::new() $o.Id = '/subscriptions/sub1/rg/vm-pipe' $o.Type = 'microsoft.compute/virtualmachines' $o.Name = 'vm-pipe' $o.CollectedAt = (Get-Date).ToString('o') $o } $obj | Save-CIEMAzureArmResource $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-pipe' $result | Should -Not -BeNullOrEmpty $result.Name | Should -Be 'vm-pipe' } } Context 'Remove-CIEMAzureArmResource' { BeforeEach { Invoke-CIEMQuery -Query "DELETE FROM azure_arm_resources" New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-rm1' -Type 'microsoft.compute/virtualmachines' -Name 'vm-rm1' New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-rm2' -Type 'microsoft.compute/virtualmachines' -Name 'vm-rm2' New-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/nsg-rm1' -Type 'microsoft.network/networksecuritygroups' -Name 'nsg-rm1' } It 'Removes by -Id' { Remove-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-rm1' -Confirm:$false $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/vm-rm1' $result | Should -BeNullOrEmpty # Other resources still exist Get-CIEMAzureArmResource | Should -HaveCount 2 } It 'Removes all by -Type (bulk delete)' { Remove-CIEMAzureArmResource -Type 'microsoft.compute/virtualmachines' -Confirm:$false $vms = Get-CIEMAzureArmResource -Type 'microsoft.compute/virtualmachines' $vms | Should -BeNullOrEmpty # NSG still exists $nsgs = Get-CIEMAzureArmResource -Type 'microsoft.network/networksecuritygroups' $nsgs | Should -HaveCount 1 } It 'Removes all records with -All switch' { Remove-CIEMAzureArmResource -All -Confirm:$false $results = Get-CIEMAzureArmResource $results | Should -BeNullOrEmpty } It 'Removes via -InputObject' { $obj = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/nsg-rm1' Remove-CIEMAzureArmResource -InputObject $obj -Confirm:$false $result = Get-CIEMAzureArmResource -Id '/subscriptions/sub1/rg/nsg-rm1' $result | Should -BeNullOrEmpty } It 'No-ops when Id does not exist' { { Remove-CIEMAzureArmResource -Id '/nonexistent' -Confirm:$false } | Should -Not -Throw } } } |