Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azure script before refactoring #62

Merged
merged 3 commits into from
Oct 31, 2021
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
90 changes: 90 additions & 0 deletions scripts/lib/azure/adapter_azure_provision.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const { WebSiteManagementClientContext, WebApps, AppServicePlans } = require("@azure/arm-appservice");
const { DefaultAzureCredential } = require('@azure/identity')
const { refFromKeyVault } = require('./utils')
const { NetworkManagementClient } = require('@azure/arm-network');

class AdapterProvision {
constructor(credentials) {
this.azureCreds = new DefaultAzureCredential();
this.credentials = credentials
this.contextClient = new WebSiteManagementClientContext(this.azureCreds, credentials.subscriptionId)
this.webAppClient = new WebApps(this.contextClient)
}

async createAdapter(name, engine, secretId, secrets, provisionVariables) {
const { resourceGroupName, virtualNetworkName, appServicePlanName} = provisionVariables

const serverFarm = await this.createAppServicePlan(resourceGroupName, appServicePlanName)

const webApp = await this.webAppClient.beginCreateOrUpdate(resourceGroupName, name, {
location: 'eastus', //region
reserved: true,
serverFarmId: serverFarm.id,
siteConfig: {
linuxFxVersion: 'DOCKER|veloex/velo-external-db:latest',
},
identity: {
"type": "SystemAssigned"
}
})
await webApp.pollUntilFinished() //wait here or in adapterStatus?
await connectToVirtualNetwork(resourceGroupName, name, virtualNetworkName)
return { webApp }
}

async connectToVirtualNetwork(resourceGroupName, webAppName, virtualNetworkName){
const networkClient = new NetworkManagementClient(this.azureCreds, this.credentials.subscriptionId)
const virtualNetwork = await networkClient.virtualNetworks.get(resourceGroupName, virtualNetworkName)
const res = await this.webAppClient.createOrUpdateSwiftVirtualNetworkConnectionWithCheck(resourceGroupName, webAppName, { "subnetResourceId": virtualNetwork.subnets[0].id })
return res
}

async adapterStatus(webAppName, provisionVariables) {
const webAppResponse = await this.webAppClient.get(provisionVariables.resourceGroupName, webAppName)

return {
available: webAppResponse.state === 'Running',
serviceUrl: webAppResponse.defaultHostName,
identity: webAppResponse.identity
}
}

async postCreateAdapter(webAppName, provisionVariables) {
const { keyVaultName, resourceGroupName } = provisionVariables

await this.loadEnviormentVariables(resourceGroupName, webAppName, keyVaultName)
}

async loadEnviormentVariables(resourceGroupName, webAppName, keyVaultName) {
const result = await this.webAppClient.updateApplicationSettings(resourceGroupName, webAppName, {
properties: appServiceVariables(keyVaultName)
})
return result
}

async createAppServicePlan(resourceGroupName, appServicePlanName) {
const appServicePlansClient = new AppServicePlans(this.contextClient)
await appServicePlansClient.beginCreateOrUpdate(resourceGroupName, appServicePlanName, {
location: 'eastus', kind: 'linux', type: 'Microsoft.Web/serverfarms', reserved: true,
sku: { name: 'P1V2', tier: 'PremiumV2', size: 'P1V2', family: 'Pv2', capacity: 1 }
})
return (await appServicePlansClient.get(resourceGroupName, appServicePlanName))

}
}

const appServiceVariables = (keyVaultName) => {
return {
DB: refFromKeyVault(keyVaultName, 'DB'),
HOST: refFromKeyVault(keyVaultName, 'HOST'),
PASSWORD: refFromKeyVault(keyVaultName, 'PASSWORD'),
TYPE: refFromKeyVault(keyVaultName, 'TYPE'),
CLOUD_VENDOR: refFromKeyVault(keyVaultName, 'CLOUDVENDOR'),
SECRET_KEY: refFromKeyVault(keyVaultName, 'SECRETKEY'),
USER: refFromKeyVault(keyVaultName, 'USER'),
PORT: refFromKeyVault(keyVaultName, 'PORT'),
}
}


module.exports = AdapterProvision
89 changes: 89 additions & 0 deletions scripts/lib/azure/azure_config_writer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const { SecretClient } = require("@azure/keyvault-secrets");
const { KeyVaultManagementClient } = require("@azure/arm-keyvault");
const { SubscriptionClient } = require("@azure/arm-subscriptions");
const { DefaultAzureCredential } = require('@azure/identity')
const { NetworkManagementClient } = require('@azure/arm-network');

class ConfigWriter {
constructor(credentials) {
this.credentials =credentials
this.azureCreds = new DefaultAzureCredential();
this.keyVaultManagementClient = new KeyVaultManagementClient(this.azureCreds, credentials.subscriptionId)
this.userObjectId = credentials.userObjectId
}

async writeConfig(secretId, dbCredentials, host, connectionName, dbName, secretKey, provisionVariables, instanceName) {
const { resourceGroupName, keyVaultName, subnetName, virtualNetworkName } = provisionVariables

const client = new SubscriptionClient(this.azureCreds)

const tenantId = (await client.tenants.list())[0].tenantId
await this.createKeyVaultInstance(resourceGroupName, keyVaultName, tenantId, virtualNetworkName, subnetName, this.userObjectId)


const KVUri = "https://" + keyVaultName + ".vault.azure.net";
const secretClient = new SecretClient(KVUri, this.azureCreds);

const config = {
HOST: host,
USER: `${dbCredentials.user}@${instanceName}`,
PASSWORD: dbCredentials.passwd,
DB: dbName,
TYPE: 'mysql', //todo:replace to engine or load it not as secret,
CLOUDVENDOR: 'azr', //load it as secrert?
SECRETKEY:secretKey,
}

await this.loadSecrets(secretClient, config)

return;
}

async updateAccessPolicy({identity},provisionVariables){
const {resourceGroupName, keyVaultName} = provisionVariables
await this.keyVaultManagementClient.vaults.updateAccessPolicy(resourceGroupName, keyVaultName, 'add', {
location: 'eastus',
properties: {
accessPolicies: [{
objectId: identity.principalId,
tenantId: identity.tenantId,
permissions: {secrets: ["get"]}
}]
}
})

}

async createKeyVaultInstance(resourceGroupName, keyVaultName, tenantId, virtualNetworkName, subnetName, userObjectId) {
const networkClient = new NetworkManagementClient(this.azureCreds,this.credentials.subscriptionId)
const virtualNetwork = await networkClient.virtualNetworks.get(resourceGroupName, virtualNetworkName)

const keyVault = await this.keyVaultManagementClient.vaults.beginCreateOrUpdate(resourceGroupName, keyVaultName, {
"location": "eastus",
properties: {
"tenantId": tenantId,
"sku": { "name": "standard" },
"networkAcls": { "virtualNetworkRules": [{ "id": `${virtualNetwork.id}/subnets/${subnetName}` }] },
"accessPolicies":
[
{
"objectId": userObjectId, "tenantId": tenantId,
"permissions": { "secrets": ["backup", "delete", "set", "list", "get"] }
}
]
}
})
await keyVault.pollUntilFinished()
return keyVault
}

async loadSecrets(secretClient, environmentVariables) {
Object.keys(environmentVariables).forEach(async (secretName) => {
await secretClient.setSecret(secretName, environmentVariables[secretName])
})

return;
}
}

module.exports = ConfigWriter
55 changes: 55 additions & 0 deletions scripts/lib/azure/azure_vnet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { NetworkManagementClient } = require('@azure/arm-network');
const { DefaultAzureCredential } = require('@azure/identity')

class VirtualNetwork {
constructor(credentials, engine) {
this.networkClient = new NetworkManagementClient(new DefaultAzureCredential(), credentials.subscriptionId)
this.engine = engine
}

async createVirtualNetwork(resourceGroupName, virtualNetworkName, subnetName) {
await this.createVirtualNetworkInstance(resourceGroupName, virtualNetworkName)
await this.createSubnet(resourceGroupName, virtualNetworkName, subnetName)
}

async createVirtualNetworkInstance(resourceGroupName, virtualNetworkName) {
const virtualNetwork = await this.networkClient
.virtualNetworks
.beginCreateOrUpdate(resourceGroupName, virtualNetworkName,
{
location: 'eastus',
addressSpace: { addressPrefixes: ['10.0.0.0/16'] }
}
)
await virtualNetwork.pollUntilFinished();
}

async createSubnet(resourceGroupName, virtualNetworkName, subnetName) {
const subnet = await this.networkClient
.subnets
.beginCreateOrUpdate(resourceGroupName, virtualNetworkName, subnetName,
{
serviceEndpoints:
[{
service: this.engine.subnetService(),
locations: ['eastus']
},
{ service: 'Microsoft.KeyVault', locations: ['eastus'] }],
delegations:
[{
serviceName: 'Microsoft.Web/serverFarms',
name: 'Microsoft.Web/serverFarms'
}],
addressPrefix: '10.0.0.0/24'
}
)
}

async getVirtualNetwork(resourceGroupName, virtualNetworkName) {
return await this.networkClient.virtualNetworks.get(resourceGroupName, virtualNetworkName)
}
}



module.exports = VirtualNetwork
8 changes: 8 additions & 0 deletions scripts/lib/azure/db/factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const clientFor = engine => {
switch (engine) {
case 'mysql':
return require('./mysql_support')
}
}

module.exports = { clientFor }
26 changes: 26 additions & 0 deletions scripts/lib/azure/db/mysql_support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { MySQLManagementClient, MySQLManagementClientContext, Servers, Databases, VirtualNetworkRules } = require("@azure/arm-mysql");

const serversClient = contextClient => new Servers(contextClient)

const managementClient = (credentials, subscriptionId) => new MySQLManagementClient(credentials, subscriptionId)

const contextClient = (credentials, subscriptionId) => new MySQLManagementClientContext(credentials, subscriptionId)


const createInitDb = async (resourceGroupName, serverName, databaseName, contextClient) => {
const dataBases = new Databases(contextClient);
const db = await dataBases.beginCreateOrUpdate(resourceGroupName, serverName, databaseName, dataBases);
await db.pollUntilFinished()
}

const subnetService = () => 'Microsoft.Sql'

const createVirtualNetworkRule = async (serverName, resourceGroupName, virtualNetwork) => {
const virtualNetworkRulesClient = new VirtualNetworkRules(contextClient);
const virtualNetworkRule = await virtualNetworkRulesClient.beginCreateOrUpdate(resourceGroupName, serverName, virtualNetwork.name, { "virtualNetworkSubnetId": virtualNetwork.subnets[0].id })
await virtualNetworkRule.pollUntilFinished()
}

module.exports = {
serversClient, managementClient, contextClient, createInitDb,subnetService, createVirtualNetworkRule
}
76 changes: 76 additions & 0 deletions scripts/lib/azure/db_azure_provision.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const { ResourceManagementClientContext, ResourceGroups } = require('@azure/arm-resources');
const { DefaultAzureCredential } = require('@azure/identity')
const VirtualNetwork = require ('./azure_vnet')
const factory = require('./db/factory')

class DbProvision {
constructor(credentials, region, engine) { //todo : get here region and transfer it to azure region
this.azureCreds = new DefaultAzureCredential();
this.credentials = credentials
this.engine = factory.clientFor(engine)
this.managementClient = this.engine.managementClient(this.azureCreds, this.credentials.subscriptionId)
this.contextClient = this.engine.contextClient(this.azureCreds, this.credentials.subscriptionId)
this.serversClient = this.engine.serversClient(this.contextClient)
this.VirtualNetworkService = new VirtualNetwork(credentials, this.engine)
}


async createDb({ name, credentials, resourceGroupName }) {
try {
const response = await this.serversClient.beginCreate(resourceGroupName, name, {
properties:
{
createMode: 'Default', administratorLogin: credentials.user,
administratorLoginPassword: credentials.passwd, sslEnforcement: 'Disabled'
},
location: 'eastus', sku: { tier: 'GeneralPurpose', name: 'GP_Gen5_4' }
})

// response.pollUntilFinished() // wait here or in dbStatusAvailable?
return true;
}
catch (e) {
console.log(e);
return false
}
}

async dbStatusAvailable(name, provisionVariables) {
try {
const instance = await this.serversClient.get(provisionVariables.resourceGroupName, name)
return {
instanceName: name,
host: instance.fullyQualifiedDomainName,
available: instance.userVisibleState === 'Ready'
}
} catch (e) {
return { available: false }
}
}


async preCreateDb(provisionVariables) {
const { resourceGroupName, virtualNetworkName, subnetName } = provisionVariables
await this.createResourceGroup(resourceGroupName)
await this.VirtualNetworkService.createVirtualNetwork(resourceGroupName, virtualNetworkName, subnetName)
}


async postCreateDb(engine, dbName, status, dbCredentials, provisionVariables, instanceName) {
await this.engine.createInitDb(provisionVariables.resourceGroupName, instanceName,dbName,this.contextClient)
await this.createVirtualNetworkRule(instanceName, provisionVariables.resourceGroupName)
}

async createResourceGroup(resourceGroupName) {
const resourceGroupsClient = new ResourceGroups(new ResourceManagementClientContext(this.azureCreds, this.credentials.subscriptionId));
const resourceGroup = await resourceGroupsClient.createOrUpdate(resourceGroupName, { location: 'eastus' })
return resourceGroup;
}

async createVirtualNetworkRule(serverName, resourceGroupName) {
const virtualNetwork = this.VirtualNetworkService
.getVirtualNetwork(resourceGroupName, virtualNetworkName)
this.engine.createVirtualNetworkRule(serverName, resourceGroupName, virtualNetwork)
}
}
module.exports = DbProvision
Loading