Skip to content

Commit

Permalink
Merge pull request #272 from Azure/gb-tfspike
Browse files Browse the repository at this point in the history
Wrapped in Terraform - preview
  • Loading branch information
Gordon Byers authored May 16, 2022
2 parents 4a79cd1 + 0b2a7d7 commit c6ce724
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 18 deletions.
6 changes: 3 additions & 3 deletions bicep/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -1176,16 +1176,16 @@ resource aks_policies 'Microsoft.Authorization/policyAssignments@2020-09-01' = i
}

@description('The principal ID to assign the AKS admin role.')
param adminprincipleid string = ''
param adminPrincipalId string = ''
// for AAD Integrated Cluster wusing 'enableAzureRBAC', add Cluster admin to the current user!
var buildInAKSRBACClusterAdmin = resourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')
resource aks_admin_role_assignment 'Microsoft.Authorization/roleAssignments@2021-04-01-preview' = if (enableAzureRBAC && !empty(adminprincipleid)) {
resource aks_admin_role_assignment 'Microsoft.Authorization/roleAssignments@2021-04-01-preview' = if (enableAzureRBAC && !empty(adminPrincipalId)) {
scope: aks // Use when specifying a scope that is different than the deployment scope
name: guid(aks.id, 'aksadmin', buildInAKSRBACClusterAdmin)
properties: {
roleDefinitionId: buildInAKSRBACClusterAdmin
principalType: 'User'
principalId: adminprincipleid
principalId: adminPrincipalId
}
}

Expand Down
9 changes: 8 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"aadgroupids",
"accv",
"ACRNAME",
"adminprincipleid",
"AGIC",
"aksc",
"aksvnetparams",
"allok",
"apiips",
Expand All @@ -22,8 +22,10 @@
"APPNAME",
"autoscale",
"Autoscaler",
"azcontext",
"azurecr",
"azurepolicy",
"azurerm",
"bitnami",
"Burstable",
"BYOVNET",
Expand Down Expand Up @@ -53,8 +55,11 @@
"ghpages",
"gitops",
"Grafana",
"hashicorp",
"ilbsub",
"Inconsolata",
"jsondecode",
"jsonencode",
"Jumpboxes",
"keyvault",
"khowling",
Expand Down Expand Up @@ -102,6 +107,8 @@
"TEMPLATERELEASE",
"templating",
"testid",
"Tfcmd",
"tfplan",
"urlname",
"userauth",
"valres",
Expand Down
19 changes: 16 additions & 3 deletions helper/src/components/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function getError(array, field) {
export const adv_stackstyle = { root: { border: "1px solid", margin: "10px 0", padding: "15px" } }


export function CodeBlock({deploycmd, testId}) {
export function CodeBlock({deploycmd, testId, lang, filename}) {
const [ copied, setCopied ] = useState(false)

function copyIt() {
Expand All @@ -30,14 +30,27 @@ export function CodeBlock({deploycmd, testId}) {
setTimeout(() => setCopied(false), 1000)
}

function downloadIt(){
function dataUrl(data) {return "data:x-application/text," + escape(deploycmd);}
window.open(dataUrl());
}

return [
<div key="code-header" className="codeHeader" >
<span className="language">Bash</span>
<span className="language">{lang}</span>
<CommandBarButton
//disabled={filename.length()>0}
className="action position-relative"
iconProps={{ iconName: copied? 'Completed' : 'Save'}}
styles={{icon: {color: '#171717'}}}
text="Save"
primaryActionButtonProps={{download: filename}}
onClick={downloadIt}/>
<CommandBarButton disabled={copied} className="action position-relative" iconProps={{ iconName: copied? 'Completed' : 'Copy'}} styles={{icon: {color: '#171717'}}} text="Copy" onClick={copyIt}/>
</div>,

<pre key="code-pre" className="has-inner-focus">
<code className="lang-bash"><span data-testid={testId || 'none'}>{deploycmd}</span></code>
<code className={"lang-" + lang}><span data-lang={lang} data-testid={testId || 'none'}>{deploycmd}</span></code>
</pre>
]
}
Expand Down
120 changes: 110 additions & 10 deletions helper/src/components/deployTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { Checkbox, Pivot, PivotItem, Image, TextField, Link, Separator, Dropdow

import { CodeBlock, adv_stackstyle, getError } from './common'

export default function DeployTab({ defaults, updateFn, tabValues, invalidArray, invalidTabs, urlParams }) {
export default function DeployTab({ defaults, updateFn, tabValues, invalidArray, invalidTabs, urlParams, featureFlag }) {
const terraformFeatureFlag = featureFlag.includes('tf')

const { net, addons, cluster, deploy } = tabValues

const allok = !(invalidTabs && invalidTabs.length > 0)
const apiips_array = deploy.apiips.split(',').filter(x => x.trim())
const aksvnetparams = {
Expand Down Expand Up @@ -40,7 +42,7 @@ export default function DeployTab({ defaults, updateFn, tabValues, invalidArray,
...(net.vnet_opt === "byo" && addons.ingress === 'appgw' && { byoAGWSubnetId: net.byoAGWSubnetId }),
...(cluster.enable_aad && { enable_aad: true, ...(cluster.enableAzureRBAC === false && cluster.aad_tenant_id && { aad_tenant_id: cluster.aad_tenant_id }) }),
...(cluster.enable_aad && cluster.AksDisableLocalAccounts !== defaults.cluster.AksDisableLocalAccounts && { AksDisableLocalAccounts: cluster.AksDisableLocalAccounts }),
...(cluster.enable_aad && cluster.enableAzureRBAC && { enableAzureRBAC: true, ...(deploy.clusterAdminRole && { adminprincipleid: "$(az ad signed-in-user show --query objectId --out tsv)" }) }),
...(cluster.enable_aad && cluster.enableAzureRBAC && { enableAzureRBAC: true, ...(deploy.clusterAdminRole && { adminPrincipalId: "$(az ad signed-in-user show --query objectId --out tsv)" }) }),
...(addons.registry !== "none" && {
registries_sku: addons.registry,
...(deploy.acrPushRole && { acrPushRolePrincipalId: "$(az ad signed-in-user show --query objectId --out tsv)"}) }),
Expand Down Expand Up @@ -107,7 +109,33 @@ export default function DeployTab({ defaults, updateFn, tabValues, invalidArray,
return ` \\\n\t${k}=${targetVal}`
}).join('')

const params2file = p => Object.keys(p).filter(p => p !== 'adminprincipleid' && p !== 'acrPushRolePrincipalId' && p !== 'kvOfficerRolePrincipalId').reduce((a, c) => { return { ...a, parameters: { ...a.parameters, [c]: { value: p[c] } } } }, {

const params2tf = p => Object.keys(p).map(k => {
return ` ${k} = ${k.toLowerCase().endsWith('principalid') ? 'data.azurerm_client_config.current.client_id' : `{value=var.${k}}`}\n`
}).join('')

const params2TfVar = p => Object.keys(p).filter(p => p !== 'adminPrincipalId' &&
p !== 'acrPushRolePrincipalId' &&
p !== 'kvOfficerRolePrincipalId').map(k => {

const val = p[k]

switch (typeof val) {
case "string":
return ` \nvariable ${k} {\n default="${val}"\n}`
case "number": case "boolean":
return ` \nvariable ${k} {\n default=${val}\n}`
default:
const arrayVal = Array.isArray(val) ? JSON.stringify(val) : val
console.log(k + ' ' + val + ' ' + typeof val);
return ` \nvariable ${k} {\n default=${arrayVal}\n}`
}

}).join('')

const params2file = p => Object.keys(p).filter(p => p !== 'adminPrincipalId' &&
p !== 'acrPushRolePrincipalId' &&
p !== 'kvOfficerRolePrincipalId').reduce((a, c) => { return { ...a, parameters: { ...a.parameters, [c]: { value: p[c] } } } }, {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {}
Expand All @@ -121,7 +149,50 @@ export default function DeployTab({ defaults, updateFn, tabValues, invalidArray,
`az group create -l ${deploy.location} -n ${deploy.rg} \n\n` +
`# Deploy template with in-line parameters \n` +
`az deployment group create -g ${deploy.rg} ${deploy.selectedTemplate === "local" ? '--template-file ./bicep/main.bicep' : `--template-uri ${deploy.templateVersions.length >1 && deploy.templateVersions.find(t => t.key === deploy.selectedTemplate).url}` } --parameters` + params2CLI(finalParams)
const param_file = JSON.stringify(params2file(finalParams), null, 2).replaceAll('\\\\\\', '').replaceAll('\\\\\\', '')
const deployTfcmd = `#download the *.tf files and run these commands to deploy using terraform\n#for more AKS Construction samples of deploying with terraform, see https://aka.ms/aksc/terraform\n\nterraform init\nterraform plan -out main.tfplan\nterraform apply "main.tfplan"\nterraform output`
const deployTfProviders =
`#providers.tf\n\n` +
`terraform {\n` +
` required_version = ">=0.12"\n` +
` required_providers {\n` +
` azurerm = {\n` +
` source = "hashicorp/azurerm"\n` +
` version = "~>3.0"\n` +
` }\n` +
` }\n` +
`}\n\n` +
`provider "azurerm" {\n` +
` features {}\n` +
`}`
const deployTfMain =
`#main.tf\n\n` +
`data "http" "aksc_release" {\n` +
` url = "${deploy.templateVersions.length >1 && deploy.templateVersions.find(t => t.key === deploy.selectedTemplate).url}"\n`+
` request_headers = {\n` +
` Accept = "application/json"\n` +
` User-Agent = "request module"\n` +
` }\n` +
`}\n\n` +
`data "azurerm_client_config" "current" {}\n\n` +
`resource "azurerm_resource_group" "rg" {\n` +
` name = "${deploy.rg}"\n`+
` location = "${deploy.location}"\n` +
`}\n\n` +
`resource "azurerm_resource_group_template_deployment" "aksc_deploy" {\n` +
` name = "AKS-C"\n` +
` resource_group_name = azurerm_resource_group.rg.name\n` +
` deployment_mode = "Incremental"\n` +
` template_content = data.http.aksc_release.body\n` +
` parameters_content = jsonencode({\n` +
params2tf(finalParams) +
` })\n` +
`}`

const deployTfVar = `#variables.tf\n` + params2TfVar(finalParams)

const deployTfOutput = `#outputs.tf\n\noutput aksClusterName {\n value = jsondecode(azurerm_resource_group_template_deployment.aksc_deploy.output_content).aksClusterName.value\n}`

const param_file = JSON.stringify(params2file(finalParams), null, 2).replaceAll('\\\\\\', '').replaceAll('\\\\\\', '')


/* WIP - Want the UI to call a common post-install script, instead of outputting the individual commands!
Expand Down Expand Up @@ -385,9 +456,9 @@ ${postscript_cluster.replaceAll('"', '\\"')}
}


<Pivot >
<Pivot defaultSelectedKey={terraformFeatureFlag ? "deployTf" : "deployArmCli" } >

<PivotItem headerText="Provision Environment (CLI)" >
<PivotItem headerText="Provision Environment (CLI)" itemKey="deployArmCli" itemIcon="PasteAsCode" >

<Stack horizontal horizontalAlign="space-between" styles={{root: { width: '100%', marginTop: '10px'}}}>
<Stack.Item>
Expand Down Expand Up @@ -418,12 +489,41 @@ ${postscript_cluster.replaceAll('"', '\\"')}
{ urlParams.toString() !== "" &&
<Text variant="medium">Not ready to deploy? Bookmark your configuration : <a href={"?" + urlParams.toString()}>here</a></Text>
}
</PivotItem>

{terraformFeatureFlag && (
<PivotItem headerText="Provision Environment (Terraform)" itemKey="deployTf" itemIcon="FileCode">
<Stack horizontal horizontalAlign="space-between" styles={{root: { width: '100%', marginTop: '10px'}}}>
<Stack.Item>
<Label >Commands to deploy your fully operational environment</Label>
<Text>
Requires Terraform, or execute in the <Link target="_cs" href="http://shell.azure.com/">Azure Cloud Shell</Link>.
</Text>
</Stack.Item>

<Stack.Item align="end">
<Stack horizontal tokens={{childrenGap: 5}}>
<Label>Template Version</Label>
<Dropdown
disabled={process.env.REACT_APP_TEMPLATERELEASE}
selectedKey={deploy.selectedTemplate}
onChange={(ev, { key }) => updateFn('selectedTemplate', key)}
options={deploy.templateVersions}
styles={{ dropdown: { width: 200 } }}
/>
</Stack>
</Stack.Item>
</Stack>

<CodeBlock deploycmd={deployTfcmd} testId={'deploy-deployTfcmd'} lang="bash"/>
<CodeBlock deploycmd={deployTfProviders} testId={'deploy-deployTfProviders'} lang="terraform" filename="providers.tf" />
<CodeBlock deploycmd={deployTfMain} testId={'deploy-deployTfMain'} lang="terraform" filename="main.tf" />
<CodeBlock deploycmd={deployTfVar} testId={'deploy-deployTfVar'} lang="terraform" filename="variables.tf" />
<CodeBlock deploycmd={deployTfOutput} testId={'deploy-deployTfOut'} lang="terraform" filename="outputs.tf" />

</PivotItem>

<PivotItem headerText="Post Configuration">
</PivotItem>
)}
<PivotItem headerText="Post Configuration" itemIcon="ConfigurationSolid">
{addons.gitops === 'none' ? [

<Label key="post-label" style={{marginTop: '10px'}}>Commands to install kubernetes packages into your cluster</Label>,
Expand Down Expand Up @@ -452,7 +552,7 @@ ${postscript_cluster.replaceAll('"', '\\"')}
}
</PivotItem>

<PivotItem headerText="Template Parameters File (for CI/CD)">
<PivotItem headerText="Template Parameters File (for CI/CD)" itemIcon="FileSymlink">

<TextField value={param_file} rows={param_file.split(/\r\n|\r|\n/).length + 1} readOnly={true} label="Parameter file" styles={{ root: { fontFamily: 'SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace!important' }, field: { backgroundColor: 'lightgrey', lineHeight: '21px' } }} multiline >
</TextField>
Expand Down
2 changes: 1 addition & 1 deletion helper/src/components/portalnav.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export default function PortalNav({ config }) {

<Pivot selectedKey={pivotkey} onLinkClick={_handleLinkClick} focusZoneProps={{ 'data-testid': `portalnav-Pivot` }}>
<PivotItem headerText={tabLabels.deploy} itemKey="deploy" onRenderItemLink={(a, b) => _customRenderer('deploy', a, b)}>
<DeployTab defaults={defaults} tabValues={tabValues} updateFn={(field, value) => mergeState("deploy", field, value)} invalidArray={invalidArray['deploy']} invalidTabs={Object.keys(invalidArray).filter(t => invalidArray[t].length > 0).map(k => `'${tabLabels[k]}'`)} urlParams={urlParams} />
<DeployTab defaults={defaults} tabValues={tabValues} updateFn={(field, value) => mergeState("deploy", field, value)} invalidArray={invalidArray['deploy']} invalidTabs={Object.keys(invalidArray).filter(t => invalidArray[t].length > 0).map(k => `'${tabLabels[k]}'`)} urlParams={urlParams} featureFlag={featureFlag} />
</PivotItem>
<PivotItem headerText={tabLabels.cluster} itemKey="cluster" onRenderItemLink={(a, b) => _customRenderer('cluster', a, b)} >
<ClusterTab tabValues={tabValues} featureFlag={featureFlag} updateFn={(field, value) => mergeState("cluster", field, value)} invalidArray={invalidArray['cluster']} />
Expand Down

0 comments on commit c6ce724

Please sign in to comment.