Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 23 additions & 28 deletions src/lib/azure/services/storage/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import { DataAzurermStorageContainer } from '@cdktf/provider-azurerm/lib/data-az
import { StorageAccount } from '@cdktf/provider-azurerm/lib/storage-account'
import { StorageBlob } from '@cdktf/provider-azurerm/lib/storage-blob'
import { StorageContainer } from '@cdktf/provider-azurerm/lib/storage-container'
import { DataAzurermStorageAccountBlobContainerSas } from '@cdktf/provider-azurerm/lib/data-azurerm-storage-account-blob-container-sas'
import { CommonAzureConstruct } from '../../common'
import { createAzureTfOutput } from '../../utils'
import { StorageAccountProps, StorageBlobProps, StorageContainerProps } from './types'
import { DataAzurermStorageAccountBlobContainerSas } from '@cdktf/provider-azurerm/lib/data-azurerm-storage-account-blob-container-sas'
import {
StorageAccountProps,
StorageBlobProps,
StorageContainerProps,
DataAzurermStorageAccountBlobContainerSasProps,
} from './types'

/**
* @classdesc Provides operations on Azure Storage
Expand Down Expand Up @@ -140,13 +145,12 @@ export class AzureStorageManager {
*
* @param id - Unique scoped identifier for the SAS token resource
* @param scope - CDKTF construct scope in which the resource will be created
* @param props - Container details and SAS options:
* - storageAccountName: The name of the existing Azure Storage Account
* - storageContainerName: The name of the container within the storage account
* - resourceGroupName: The name of the resource group containing the storage account
* - sasStart: Optional start date in the format 'YYYY-MM-DD'. If not provided, defaults to today’s date.
* @param props - SAS options:
* - start: Optional start date in the format 'YYYY-MM-DD'. If not provided, defaults to today’s date.
* To avoid diffs on every deploy, it is recommended to supply a fixed value.
* - sasExpiry: Optional expiry date in the format 'YYYY-MM-DD'. Defaults to 7 days from current date if not provided.
* - expiry: Optional expiry date in the format 'YYYY-MM-DD'. Defaults to 7 days from current date if not provided.
* @param storageAccount
* @param storageContainer
*
* @returns A `DataAzurermStorageAccountBlobContainerSas` instance with the generated SAS token
*
Expand All @@ -155,26 +159,17 @@ export class AzureStorageManager {
public generateContainerSasToken(
id: string,
scope: CommonAzureConstruct,
props: {
storageAccountName: string
storageContainerName: string
resourceGroupName: string
sasStart?: string
sasExpiry?: string
}
): DataAzurermStorageAccountBlobContainerSas {
const storageAccountLookup = new DataAzurermStorageAccount(scope, `${id}-lookup-sa`, {
name: props.storageAccountName,
resourceGroupName: props.resourceGroupName,
})

const containerSas = new DataAzurermStorageAccountBlobContainerSas(scope, `${id}-sas`, {
connectionString: storageAccountLookup.primaryConnectionString,
containerName: props.storageContainerName,
httpsOnly: true,
start: props.sasStart ?? new Date().toISOString().split('T')[0],
expiry: props.sasExpiry ?? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
permissions: {
props: DataAzurermStorageAccountBlobContainerSasProps,
storageAccount: StorageAccount,
storageContainer?: StorageContainer
) {
const containerSas = new DataAzurermStorageAccountBlobContainerSas(scope, `${id}-sc-sas`, {
connectionString: storageAccount.primaryConnectionString,
containerName: props.containerName ?? storageContainer?.name,
httpsOnly: props.httpsOnly ?? true,
start: props.start ?? new Date().toISOString().split('T')[0],
expiry: props.expiry ?? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
permissions: props.permissions ?? {
read: true,
add: false,
create: false,
Expand Down
12 changes: 5 additions & 7 deletions src/lib/azure/services/storage/types.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { StorageAccountConfig } from '@cdktf/provider-azurerm/lib/storage-account'
import { StorageBlobConfig } from '@cdktf/provider-azurerm/lib/storage-blob'
import { StorageContainerConfig } from '@cdktf/provider-azurerm/lib/storage-container'
import { DataAzurermStorageAccountBlobContainerSasConfig } from '@cdktf/provider-azurerm/lib/data-azurerm-storage-account-blob-container-sas'
import { BaseAzureConfigProps } from '../../types'

export interface StorageAccountProps extends StorageAccountConfig {}

export interface StorageContainerProps extends BaseAzureConfigProps, StorageContainerConfig {}

export interface StorageBlobProps extends BaseAzureConfigProps, StorageBlobConfig {
/**
* Optional ISO date string representing the expiry date for the SAS token.
* Format: 'YYYY-MM-DD' (e.g., '2025-05-01')
*/
sasExpiry?: string
}
export interface StorageBlobProps extends BaseAzureConfigProps, StorageBlobConfig {}

export interface DataAzurermStorageAccountBlobContainerSasProps
extends DataAzurermStorageAccountBlobContainerSasConfig {}
4 changes: 4 additions & 0 deletions src/test/azure/common/cdkConfig/storage.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@
"name": "test-storage-blob",
"storageAccountName": "test-storage-account",
"storageContainerName": "test-storage-container"
},
"testContainerSas": {
"expiry": "2040-12-31",
"permissions": { "add": true, "create": true, "delete": true, "list": true, "read": true, "write": true }
}
}
30 changes: 0 additions & 30 deletions src/test/azure/common/common-azure-construct.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
StorageBlobProps,
StorageContainerProps,
} from '../../../lib'
import { DataAzurermStorageAccountBlobContainerSas } from '@cdktf/provider-azurerm/lib/data-azurerm-storage-account-blob-container-sas'

interface TestAzureStackProps extends CommonAzureStackProps {
testStorageAccount: StorageAccountProps
Expand Down Expand Up @@ -67,12 +66,6 @@ class TestCommonConstruct extends CommonAzureConstruct {
this.props.testStorageContainer
)
this.storageManager.createStorageBlob(`test-storage-blob-${this.props.stage}`, this, this.props.testStorageBlob)

this.storageManager.generateContainerSasToken(`test-container-sas-token-${this.props.stage}`, this, {
storageAccountName: this.props.testStorageAccount.name,
storageContainerName: this.props.testStorageContainer.name,
resourceGroupName: this.props.testStorageAccount.resourceGroupName ?? 'test-rg-dev',
})
}
}

Expand Down Expand Up @@ -191,26 +184,3 @@ describe('TestAzureCommonConstruct', () => {
})
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions SAS output as expected', () => {
expect(JSON.parse(construct).output).toHaveProperty('testContainerSasTokenDevSasToken')
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions container SAS token as expected', () => {
expect(construct).toHaveDataSourceWithProperties(DataAzurermStorageAccountBlobContainerSas, {
container_name: 'test-storage-container',
https_only: true,
permissions: {
read: true,
add: false,
create: false,
delete: false,
list: false,
write: false,
},
})
})
})
224 changes: 224 additions & 0 deletions src/test/azure/services/storage-manager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { StorageAccount } from '@cdktf/provider-azurerm/lib/storage-account'
import { StorageBlob } from '@cdktf/provider-azurerm/lib/storage-blob'
import { StorageContainer } from '@cdktf/provider-azurerm/lib/storage-container'
import { App, Testing } from 'cdktf'
import 'cdktf/lib/testing/adapters/jest'
import { Construct } from 'constructs'
import {
CommonAzureConstruct,
CommonAzureStack,
CommonAzureStackProps,
StorageAccountProps,
StorageBlobProps,
StorageContainerProps,
DataAzurermStorageAccountBlobContainerSasProps,
} from '../../../lib'
import { DataAzurermStorageAccountBlobContainerSas } from '@cdktf/provider-azurerm/lib/data-azurerm-storage-account-blob-container-sas'

interface TestAzureStackProps extends CommonAzureStackProps {
testStorageAccount: StorageAccountProps
testStorageContainer: StorageContainerProps
testStorageBlob: StorageBlobProps
testContainerSas: DataAzurermStorageAccountBlobContainerSasProps
testAttribute?: string
}

const testStackProps: any = {
domainName: 'gradientedge.io',
extraContexts: ['src/test/azure/common/cdkConfig/dummy.json', 'src/test/azure/common/cdkConfig/storage.json'],
features: {},
name: 'test-common-stack',
resourceGroupName: 'test-rg',
skipStageForARecords: false,
stage: 'dev',
stageContextPath: 'src/test/aws/common/cdkEnv',
}

class TestCommonStack extends CommonAzureStack {
declare props: TestAzureStackProps

constructor(parent: Construct, name: string, props: TestAzureStackProps) {
super(parent, name, testStackProps)
this.construct = new TestCommonConstruct(this, props.name, this.props)
}

protected determineConstructProps(props: CommonAzureStackProps) {
return {
...super.determineConstructProps(props),
testAttribute: this.node.tryGetContext('testAttribute'),
testStorageAccount: this.node.tryGetContext('testStorageAccount'),
testStorageBlob: this.node.tryGetContext('testStorageBlob'),
testStorageContainer: this.node.tryGetContext('testStorageContainer'),
testContainerSas: this.node.tryGetContext('testContainerSas'),
}
}
}

class TestCommonConstruct extends CommonAzureConstruct {
declare props: TestAzureStackProps

constructor(parent: Construct, name: string, props: TestAzureStackProps) {
super(parent, name, props)
const storageAccount = this.storageManager.createStorageAccount(
`test-storage-account-${this.props.stage}`,
this,
this.props.testStorageAccount
)
const storageContainer = this.storageManager.createStorageContainer(
`test-storage-container-${this.props.stage}`,
this,
this.props.testStorageContainer
)
this.storageManager.createStorageBlob(`test-storage-blob-${this.props.stage}`, this, this.props.testStorageBlob)

this.storageManager.generateContainerSasToken(
`test-container-sas-token-${this.props.stage}`,
this,
this.props.testContainerSas,
storageAccount,
storageContainer
)
}
}

const app = new App({ context: testStackProps })
const testingApp = Testing.fakeCdktfJsonPath(app)
const commonStack = new TestCommonStack(testingApp, 'test-common-stack', testStackProps)
const stack = Testing.fullSynth(commonStack)
const construct = Testing.synth(commonStack.construct)

describe('TestAzureCommonConstruct', () => {
test('is initialised as expected', () => {
/* test if the created stack have the right properties injected */
expect(commonStack.props).toHaveProperty('testAttribute')
expect(commonStack.props.testAttribute).toEqual('success')
})
})

describe('TestAzureCommonConstruct', () => {
test('synthesises as expected', () => {
expect(stack).toBeDefined()
expect(construct).toBeDefined()
expect(stack).toBeValidTerraform()
expect(stack).toPlanSuccessfully()
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions outputs as expected', () => {
expect(JSON.parse(construct).output).toMatchObject({
testStorageAccountDevStorageAccountFriendlyUniqueId: {
value: 'test-storage-account-dev-sa',
},
testStorageAccountDevStorageAccountId: {
value: '${azurerm_storage_account.test-storage-account-dev-sa.id}',
},
testStorageAccountDevStorageAccountName: {
value: '${azurerm_storage_account.test-storage-account-dev-sa.name}',
},
testStorageBlobDevStorageBlobFriendlyUniqueId: {
value: 'test-storage-blob-dev-sb',
},
testStorageBlobDevStorageBlobId: {
value: '${azurerm_storage_blob.test-storage-blob-dev-sb.id}',
},
testStorageBlobDevStorageBlobName: {
value: '${azurerm_storage_blob.test-storage-blob-dev-sb.name}',
},
testStorageContainerDevStorageContainerFriendlyUniqueId: {
value: 'test-storage-container-dev-sc',
},
testStorageContainerDevStorageContainerId: {
value: '${azurerm_storage_container.test-storage-container-dev-sc.id}',
},
testStorageContainerDevStorageContainerName: {
value: '${azurerm_storage_container.test-storage-container-dev-sc.name}',
},
})
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions data as expected', () => {
expect(JSON.parse(construct).data).toMatchObject({
azurerm_resource_group: {
'test-storage-account-dev-sa-rg': {
name: 'test-rg-dev',
},
'test-storage-blob-dev-sb-rg': {
name: 'test-rg-dev',
},
},
azurerm_storage_account: {
'test-storage-blob-dev-sa': {
name: 'test-storage-account-dev',
resource_group_name: '${data.azurerm_resource_group.test-storage-blob-dev-sb-rg.name}',
},
},
azurerm_storage_container: {
'test-storage-blob-dev-sc': {
name: 'test-storage-container-dev',
},
},
})
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions storage account as expected', () => {
expect(construct).toHaveResourceWithProperties(StorageAccount, {
account_tier: 'Standard',
location: '${data.azurerm_resource_group.test-storage-account-dev-sa-rg.location}',
name: 'teststorageaccountdev',
resource_group_name: '${data.azurerm_resource_group.test-storage-account-dev-sa-rg.name}',
tags: {
environment: 'dev',
},
})
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions storage container as expected', () => {
expect(construct).toHaveResourceWithProperties(StorageContainer, {
name: 'test-storage-container-dev',
storage_account_name: 'test-storage-account',
})
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions storage blob as expected', () => {
expect(construct).toHaveResourceWithProperties(StorageBlob, {
name: 'test-storage-blob-dev',
storage_account_name: '${data.azurerm_storage_account.test-storage-blob-dev-sa.name}',
storage_container_name: '${data.azurerm_storage_container.test-storage-blob-dev-sc.name}',
})
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions SAS output as expected', () => {
expect(JSON.parse(construct).output).toHaveProperty('testContainerSasTokenDevSasToken')
})
})

describe('TestAzureCommonConstruct', () => {
test('provisions container SAS token as expected', () => {
expect(construct).toHaveDataSourceWithProperties(DataAzurermStorageAccountBlobContainerSas, {
connection_string: '${azurerm_storage_account.test-storage-account-dev-sa.primary_connection_string}',
container_name: '${azurerm_storage_container.test-storage-container-dev-sc.name}',
expiry: '2040-12-31',
https_only: true,
permissions: {
add: true,
create: true,
delete: true,
list: true,
read: true,
write: true,
},
start: expect.any(String),
})
})
})