initial commit
commit
a79cac8cd8
|
|
@ -0,0 +1,214 @@
|
|||
## Azure Information
|
||||
|
||||
#### tenant
|
||||
|
||||
67bda7ee-fd80-41ef-ac91-358418290a1e # nottingham
|
||||
|
||||
#### subscriptions
|
||||
GBUoN-uks-Dev 8a6722e9-035b-4b46-9408-ff040ff063e2 # nottingham dev
|
||||
|
||||
Production 1a3c8479-0046-4a6f-a2ad-397cb9a6f931 # limited access to use / list vnet+subnet and obtain ip (prod network)
|
||||
|
||||
Research Managed 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c # nottingham research (prod)
|
||||
|
||||
#### research (prod) resource groups
|
||||
|
||||
rg-svc-rem-we-spp-1 # where the azure instances, vnic and resources reside
|
||||
rg-vn-rem-we-1 # where the vnet+subnet reside for the vnic
|
||||
|
||||
#### dev resource group
|
||||
|
||||
UI-SPP-DEV-001 # where the azure instances, vnic and resources reside
|
||||
|
||||
## login / get bearer token to the tenant (dev)
|
||||
|
||||
When provisioning resources in the GBUoN-uks-Dev subscription ensure your account has been delegated the contributor role to resource group UI-SPP-DEV-001.
|
||||
This method of authorization will give a URL that accepts the displayed device code then ask to authenticate (in this case with your nottingham.ac.uk account)
|
||||
|
||||
az login --tenant 67bda7ee-fd80-41ef-ac91-358418290a1e --use-device-code
|
||||
|
||||
SPN id: f45a0e1e-3f7f-44e8-971d-a56b563ef589
|
||||
SPN secret: I5aAwcc9bIE]]kmhPIQpp5JCK1BiTg?]
|
||||
|
||||
az login --service-principal --username f45a0e1e-3f7f-44e8-971d-a56b563ef589 --password "I5aAwcc9bIE]]kmhPIQpp5JCK1BiTg?]" --tenant 67bda7ee-fd80-41ef-ac91-358418290a1e
|
||||
|
||||
## login / get bearer token using service principal credentials for the tenant (prod)
|
||||
|
||||
SPN id: ccb8dfce-f33d-4b09-9ab3-5acc1c43f368
|
||||
SPN secret: 622zlS45N.6_q.f6hy~5zgL.F53M6we7Qg
|
||||
|
||||
az login --service-principal --username ccb8dfce-f33d-4b09-9ab3-5acc1c43f368 --password "622zlS45N.6_q.f6hy~5zgL.F53M6we7Qg" --tenant 67bda7ee-fd80-41ef-ac91-358418290a1e
|
||||
|
||||
## switch default subscription
|
||||
|
||||
az account set --subscription 8a6722e9-035b-4b46-9408-ff040ff063e2 # nottingham dev
|
||||
az account set --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c # nottingham research (prod)
|
||||
|
||||
## list available locations for subscription
|
||||
|
||||
Prod will use default location of "westeurope", ARM templates expect the display name "West Europe".
|
||||
Dev will use default location of "uksouth", ARM templates expect the display name "UK South".
|
||||
|
||||
az account list-locations
|
||||
|
||||
## find resources in subscriptions
|
||||
|
||||
az group list # whichever subscription is set
|
||||
az group list --subscription 8a6722e9-035b-4b46-9408-ff040ff063e2 # nottingham dev
|
||||
az group list --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c # nottingham research (prod)
|
||||
|
||||
## find vnet in resource group
|
||||
|
||||
Prod vnet vn-rem-we-1 in resource group rg-vn-rem-we-1, this is used for ip address on vnics, no public ip's maybe allocated.
|
||||
Research (prod) rg-svc-rem-we-spp-1 should not contain any vnet or accociated subnet, if one is created public ip's can be allocated but ensure an nsg is not shared for hosts with nics in different resource group vnets.
|
||||
|
||||
az network vnet list | jq . # we see vnet in vn-rem-we-1
|
||||
# we see subnets rg-vn-rem-we-1 | sn-vn-rem-we-1-frontend-1 | AzureBastionSubnet | sn-vn-rem-we-1-midtier-1 | vn-man-we-1, SPN may only use sn-vn-rem-we-1-midtier-1
|
||||
az network vnet list --resource-group rg-svc-rem-we-spp-1
|
||||
|
||||
## Create Azure storage blob to host installer scripts for prod 1.1 ARM templates
|
||||
|
||||
This is not required for the dev / prod 1.0 ARM templates, this change was to facilitate the carbon black install in the latest prod 1.1 ARM templates for only Windows Server and Ubuntu Server.
|
||||
|
||||
#### Create storage account
|
||||
|
||||
```
|
||||
az storage account create --name extensionartefact --resource-group rg-svc-rem-we-spp-1 --location "West Europe" --sku Standard_ZRS --https-only true --min-tls-version TLS1_2 --publish-internet-endpoints false --publish-microsoft-endpoints true --routing-choice MicrosoftRouting --encryption-services blob --tags 'CFManaged=false'
|
||||
|
||||
```
|
||||
#### Create blob container specifying auth mode
|
||||
|
||||
```
|
||||
az storage container create --resource-group rg-svc-rem-we-spp-1 --public-access off --account-name extensionartefact --name extensionartefact --auth-mode key
|
||||
az storage container list --account-name extensionartefact
|
||||
|
||||
```
|
||||
#### Upload contents of the extensionartefact directory in this repo to the container
|
||||
|
||||
Retrieve a storage account key: `az storage account keys list --resource-group rg-svc-rem-we-spp-1 --account-name extensionartefact`
|
||||
|
||||
```
|
||||
az storage blob upload-batch --destination extensionartefact --source /\<local-path-to-artefacts\>/extensionartefacts --account-name extensionartefact --account-key "\<place-key-here\>"
|
||||
|
||||
```
|
||||
#### Setup managed identity
|
||||
|
||||
```
|
||||
az identity create --name extensionartefact --resource-group rg-svc-rem-we-spp-1 --location "West Europe"
|
||||
az identity list
|
||||
az identity show --resource-group rg-svc-rem-we-spp-1 --name extensionartefact
|
||||
|
||||
```
|
||||
|
||||
#### Assign managed identity to container or storage account
|
||||
|
||||
https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-cli
|
||||
|
||||
This managed identity will be used with the ARM templates to download the custom script extension script and also download the carbon black package from the container during script runtime.
|
||||
|
||||
* Retrieve the clientId of the managed identity for the assignee parameter: `az identity show --resource-group rg-svc-rem-we-spp-1 --name extensionartefact`
|
||||
* Check name of desired role definition, only "Storage Blob Data Reader" should be required in this scenario: `az role definition list`
|
||||
* Retrieve the id of the managed identity for the scope paramter: `az storage account list`
|
||||
|
||||
```
|
||||
az role assignment create --assignee "87360148-9f10-45a2-a5ce-32d7c1134bd8" --role "Storage Blob Data Reader" --scope "/subscriptions/5d0ffc51-cdb8-4de5-a76c-fca19f5b300c/resourceGroups/rg-svc-rem-we-spp-1/providers/Microsoft.Storage/storageAccounts/extensionartefact"
|
||||
|
||||
```
|
||||
|
||||
The SPN will have insufficient privilidges to assign the rights, use your UoN account to obtain a bearer token to authenticate to complete the command.
|
||||
|
||||
## Deploy ARM templates from command line
|
||||
|
||||
Ensure you are logged into the correct tenant and using the desired subscription.
|
||||
|
||||
To test prod subscription functionality run the templates with the following minimal parameters;
|
||||
|
||||
az deployment group create --resource-group rg-svc-rem-we-spp-1 --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c --parameters "{\"adminUsername\": {\"value\": \"uonadmin\"}, \"adminPassword\": {\"value\": \"Z3qiaFJcbH2EC5DletL9\"},\"location\": {\"value\": \"West Europe\"}, \"networkResourceGroup\": {\"value\": \"rg-vn-rem-we-1\"}, \"networkSecurityGroupName\": {\"value\": \"CFLinux\"}, \"virtualNetworkName\": {\"value\": \"vn-rem-we-1\"}, \"subnetName\": {\"value\": \"sn-vn-rem-we-1-midtier-1\"}}" --template-file Azure_RHEL_instance.json
|
||||
|
||||
To test prod subscription functionality run the templates with all parameters to replicate CloudForms invocation;
|
||||
|
||||
az deployment group create --resource-group rg-svc-rem-we-spp-1 --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c --parameters "{\"adminUsername\": {\"value\": \"uonadmin\"}, \"adminPassword\": {\"value\": \"Z3qiaFJcbH2EC5DletL9\"},\"location\": {\"value\": \"West Europe\"}, \"email\": {\"value\": \"ucats@exmail.nottingham.ac.uk\"}, \"networkResourceGroup\": {\"value\": \"rg-vn-rem-we-1\"}, \"networkSecurityGroupName\": {\"value\": \"CFLinux\"}, \"virtualNetworkName\": {\"value\": \"vn-rem-we-1\"}, \"subnetName\": {\"value\": \"sn-vn-rem-we-1-midtier-1\"},\"imageUrn\": {\"value\": \"RedHat:RHEL:7.8:latest\"},\"projectCode\": {\"value\": \"UoN1234\"}, \"toggleShutdownSchedule\": {\"value\": \"t\"}, \"vmSize\": {\"value\": \"Standard_B4s\"}, \"location\": {\"value\": \"West Europe\"}, \"dataDiskType\": {\"value\": \"StandardSSD_LRS\"}, \"dataDiskSizeGB\": {\"value\": \"512\"}}" --template-file Azure_RHEL_instance.json
|
||||
|
||||
az deployment group create --resource-group rg-svc-rem-we-spp-1 --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c --parameters "{\"adminUsername\": {\"value\": \"uonadmin\"}, \"adminPassword\": {\"value\": \"Z3qiaFJcbH2EC5DletL9\"},\"location\": {\"value\": \"West Europe\"}, \"email\": {\"value\": \"ucats@exmail.nottingham.ac.uk\"}, \"networkResourceGroup\": {\"value\": \"rg-vn-rem-we-1\"}, \"networkSecurityGroupName\": {\"value\": \"CFLinux\"}, \"virtualNetworkName\": {\"value\": \"vn-rem-we-1\"}, \"subnetName\": {\"value\": \"sn-vn-rem-we-1-midtier-1\"},\"imageUrn\": {\"value\": \"Canonical:UbuntuServer:18.04-LTS:latest\"},\"projectCode\": {\"value\": \"UoN1234\"}, \"toggleShutdownSchedule\": {\"value\": \"t\"}, \"vmSize\": {\"value\": \"Standard_D8s_v3\"}, \"location\": {\"value\": \"West Europe\"}, \"dataDiskType\": {\"value\": \"StandardSSD_LRS\"}, \"dataDiskSizeGB\": {\"value\": \"512\"}}" --template-file Azure_UbuntuServer_instance.json
|
||||
|
||||
az deployment group create --resource-group rg-svc-rem-we-spp-1 --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c --parameters "{\"adminUsername\": {\"value\": \"uonadmin\"}, \"adminPassword\": {\"value\": \"Z3qiaFJcbH2EC5DletL9\"},\"location\": {\"value\": \"West Europe\"}, \"email\": {\"value\": \"ucats@exmail.nottingham.ac.uk\"}, \"networkResourceGroup\": {\"value\": \"rg-vn-rem-we-1\"}, \"networkSecurityGroupName\": {\"value\": \"CFWindows\"}, \"virtualNetworkName\": {\"value\": \"vn-rem-we-1\"}, \"subnetName\": {\"value\": \"sn-vn-rem-we-1-midtier-1\"},\"imageUrn\": {\"value\": \"MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest\"},\"projectCode\": {\"value\": \"UoN1234\"}, \"toggleShutdownSchedule\": {\"value\": \"t\"}, \"vmSize\": {\"value\": \"Standard_NC12\"}, \"location\": {\"value\": \"West Europe\"}, \"dataDiskType\": {\"value\": \"StandardSSD_LRS\"}, \"dataDiskSizeGB\": {\"value\": \"512\"}}" --template-file Azure_WindowsServer_instance.json
|
||||
|
||||
To test prod subscription functionality of a security group template to replicate CloudForms invocation;
|
||||
|
||||
az deployment group create --resource-group rg-svc-rem-we-spp-1 --subscription 5d0ffc51-cdb8-4de5-a76c-fca19f5b300c --parameters "{\"location\": {\"value\": \"West Europe\"}, \"networkSecurityGroupName\": {\"value\": \"rg-vn-rem-we-1\"}}" --template-file CFLinux_Azure_network_security_group.json
|
||||
|
||||
To test dev subscription functionality run the templates with all parameters to replicate CloudForms invocation;
|
||||
|
||||
az deployment group create --resource-group UI-SPP-DEV-001 --subscription 8a6722e9-035b-4b46-9408-ff040ff063e2 --parameters "{\"location\": {\"value\": \"UK South\"}, \"virtualNetworkName\": {\"value\": \"UI-SPP-DEV-001-vnet\"}, \"subnetName\": {\"value\": \"default\"}, \"networkSecurityGroupName\": {\"value\": \"CFLinux\"}, \"adminUsername\": {\"value\": \"uonadmin\"}, \"adminPassword\": {\"value\": \"Z3qiaFJcbH2EC5DletL9\"}, \"email\": {\"value\": \"ucats@exmail.nottingham.ac.uk\"}, \"projectCode\": {\"value\": \"1234\"}, \"toggleShutdownSchedule\": {\"value\": \"f\"}}" --template-file Azure_UbuntuServer_instance_noscript_pubip.json
|
||||
|
||||
#### parameter behaviour
|
||||
|
||||
If networkResourceGroup is ommited the template will assume the vnet+subnet reside within the same resource group as the instance.
|
||||
If email is ommited the windows ARM template will not make group membership changes or initialize the data disk, the linux ARM template will populate the sssd acl and sudoers entries with the user 'unused'.
|
||||
If projectCode is ommited the Cost Code tag will display 'not classified'.
|
||||
If toggleShutdownSchedule is ommited it assumes default value of false 'f' and no shutdown policy will be applied.
|
||||
Other parameters have default values that maybe overriden to suit the desired instance spec, however the custom script extensions are likely to fail as they are very specific to the UoN research managed environment.
|
||||
|
||||
## Template Information
|
||||
|
||||
#### Prod 1.0 Templates
|
||||
Azure_RHEL_instance.json
|
||||
Azure_UbuntuServer_instance.json
|
||||
Azure_WindowsServer_instance.json
|
||||
CFLinux_Azure_network_security_group.json linux network security group, allows ingress SSH tcp 22
|
||||
CFWindows_Azure_network_security_group.json linux network security group, allows ingress RDP tcp 3389
|
||||
Azure_UbuntuServer_customscript.sh Ubuntu prep disk, join domain, install MATE desktop, shutdown timer, nvidia drivers extension
|
||||
Azure_RHEL_customscript.sh Redhat prep disk, join domain, install MATE desktop, shutdown timer, nvidia drivers extension
|
||||
|
||||
#### Prod 1.1 Templates
|
||||
|
||||
Azure_UbuntuServer_instance.json updated ARM template to use custom script extension using blob hosted setup script Azure_UbuntuServer_customscript.sh
|
||||
Azure_WindowsServer_instance.json updated ARM template to use custom sctipt extension using blob hosted setup script Azure_WindowsServer_customscript.ps1
|
||||
extensionartefacts/ content of the blob container, setup scripts and UoN private packages such as Carbon Black and license key
|
||||
|
||||
#### Dev Templates
|
||||
|
||||
\*The dev rev1/rev2 or prod network security group templates are suitable for use in the UI-SPP-DEV-001 resource group\*
|
||||
\*The rev2 templates are equivalent to the prod templates wihtout the additional disk or shutdown timer extension they are suitable for the rg-svc-rem-we-spp-1 resource group\*
|
||||
\*The rev3 Azure_UbuntuServer_instance_noscript_pubip.json template is a backported prod template for use on the CloudForms dev appliance using the UI-SPP-DEV-001 resource group, boot scripts disabled due to not domain connectivity this is intended as a starting point for future tempate changes that need to be tested in dev first\*
|
||||
|
||||
rev1/\*templates\* templates for instance and network security group in the UI-SPP-DEV-001 resource group
|
||||
rev2/\*templates\* templates for instance and network security group in the rg-svc-rem-we-spp-1 resource group
|
||||
rev3/\*templates\* derived from prod with the addition of a public ip but scripts disabled, in the UI-SPP-DEV-001 resource group
|
||||
|
||||
#### json vs yaml
|
||||
|
||||
The templates are written in yaml and converted to json with yarn. Conversion operates both ways, it is helpful to take example json arm templates and convert to yaml.
|
||||
|
||||
Building templates in json is tedious and error prone, if a yarn sucessfully converts a template to json but does not run on the cli try an online json lint website to find syntax errors. If the template fails on the cli with no useful information run the template in Azure custom deployment for more debug, https://portal.azure.com/#create/Microsoft.Template.
|
||||
|
||||
usage;
|
||||
|
||||
https://github.com/Azure/azure-quickstart-templates Yaml allows comments and is much easier to read. https://github.com/TeamYARM/YARM-CLI
|
||||
|
||||
./Yarm.ConsoleApp.exe -i CFInstance_win.yaml
|
||||
CFInstance_win.yaml => CFInstance_win.json
|
||||
|
||||
#### Prod 1.0 customscript extensions
|
||||
|
||||
These scripts prepare the data disk, join the domain and install MATE desktop / browsers for Linux or install browsers and change key settings for Windows, they are specific to the UoN research managed environment.
|
||||
For the Prod 1.0 templates these scripts were base64 encoded and included inline within the respective templates.
|
||||
|
||||
Azure_RHEL_customscript.sh
|
||||
Azure_UbuntuServer_customscript.sh
|
||||
|
||||
#### Prod 1.1 customscript extensions
|
||||
|
||||
These scripts prepare the data disk, join the domain, install the carbon black service and install MATE desktop / browsers for Linux or install browsers and change key settings for Windows, they are specific to the UoN research managed environment.
|
||||
The templates no longer use inline scripts or commands, instead electing to use an Azure managed identity to pull the installer scripts and carbon black packages for an Azure storage blob, this was necessary as the carbon black package is not publicly avaialable and the license key file was required to be kept private.
|
||||
|
||||
extensionartefacts/Azure_UbuntuServer_customscript.sh
|
||||
extensionartefacts/Azure_WindowsServer_customscript.ps1
|
||||
|
||||
#### create customscript extension script property
|
||||
|
||||
This is only used with the Prod 1.0 Linux ARM templates, the script is base64 encoded and seeded into the template.
|
||||
|
||||
https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-linux
|
||||
|
||||
cat rhel_customscript_extension.sh | gzip -9 | base64 -w 0
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "RedHat:RHEL:7.3:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "User name for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B1ls",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "UI-SPP-DEV-001-vnet",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "default",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "UK South",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"publicIPAddressName": "[concat(variables('resourcePrefix'), '-pip')]",
|
||||
"publicIPAddressType": "Dynamic",
|
||||
"publicIpAddressSku": "Basic",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/publicIPAddresses",
|
||||
"name": "[variables('publicIPAddressName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"publicIPAddressVersion": "IPv4",
|
||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
|
||||
"idleTimeoutInMinutes": 4
|
||||
},
|
||||
"sku": {
|
||||
"name": "[variables('publicIpAddressSku')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"publicIPAddress": {
|
||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
},
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true",
|
||||
"Owner": "ucats",
|
||||
"OwnerMail": "ucats@nottingham.ac.uk",
|
||||
"CFRequestID": "1234",
|
||||
"CFServiceID": "1234",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
# cloudform issue converting json to yaml with string field containing integer (sku field), pass urn as string and split in template to workaround
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: "RedHat:RHEL:7.3:latest"
|
||||
metadata:
|
||||
description: "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: User name for the Virtual Machine.
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the Virtual Machine.
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B1ls
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: UI-SPP-DEV-001-vnet
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: default
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: UK South
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: "[utcNow()]"
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
variables:
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
publicIPAddressName: "[concat(variables('resourcePrefix'), '-pip')]"
|
||||
publicIPAddressType: Dynamic
|
||||
publicIpAddressSku: Basic
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]"
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/publicIPAddresses
|
||||
name: "[variables('publicIPAddressName')]"
|
||||
location: "[parameters('location')]"
|
||||
properties:
|
||||
publicIPAddressVersion: "IPv4"
|
||||
publicIPAllocationMethod: "[variables('publicIPAddressType')]"
|
||||
idleTimeoutInMinutes: 4
|
||||
sku:
|
||||
name: "[variables('publicIpAddressSku')]"
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
publicIPAddress:
|
||||
id: "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
subnet:
|
||||
id: "[variables('subnetRef')]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
# todo - tags to be passed by cloudforms from the options hash
|
||||
CFManaged: "true"
|
||||
Owner: ucats
|
||||
OwnerMail: ucats@nottingham.ac.uk
|
||||
CFRequestID: "1234"
|
||||
CFServiceID: "1234"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: "[deployment().name]"
|
||||
# cannot natively retrieve dynamic ip in the same deployment, need nested self referencing deployment to perform variable lookup (requiring uri to blob of this deployment template)
|
||||
# ipAddress:
|
||||
# type: string
|
||||
# value: "[reference(resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))).IpAddress]"
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "UK South",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "Name of the network security group"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "SSH",
|
||||
"properties": {
|
||||
"description": "Allow SSH traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 22,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerInBound",
|
||||
"properties": {
|
||||
"description": "allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "AzureLoadBalancer",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DenyVnetInBound",
|
||||
"properties": {
|
||||
"description": "isolate instances on the same range from each another",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Deny",
|
||||
"priority": 3996,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerOutbound",
|
||||
"properties": {
|
||||
"description": "allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "AzureLoadBalancer",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DenyVnetOutBound",
|
||||
"properties": {
|
||||
"description": "isolate instances on the same range from each another",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Deny",
|
||||
"priority": 3996,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.1
|
||||
parameters:
|
||||
location:
|
||||
type: string
|
||||
defaultValue: UK South
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: Name of the network security group
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[parameters('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: SSH
|
||||
properties:
|
||||
description: Allow SSH traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 22
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerInBound
|
||||
properties:
|
||||
description: allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'AzureLoadBalancer'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Inbound
|
||||
- name: DenyVnetInBound
|
||||
properties:
|
||||
description: isolate instances on the same range from each another
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Deny
|
||||
priority: 3996
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerOutbound
|
||||
properties:
|
||||
description: allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'AzureLoadBalancer'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Outbound
|
||||
- name: DenyVnetOutBound
|
||||
properties:
|
||||
description: isolate instances on the same range from each another
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Deny
|
||||
priority: 3996
|
||||
direction: Outbound
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "UK South",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFWindows",
|
||||
"metadata": {
|
||||
"description": "Name of the network security group"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "RDP",
|
||||
"properties": {
|
||||
"description": "Allow RDP traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 3389,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerInBound",
|
||||
"properties": {
|
||||
"description": "allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "AzureLoadBalancer",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DenyVnetInBound",
|
||||
"properties": {
|
||||
"description": "isolate instances on the same range from each another",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Deny",
|
||||
"priority": 3996,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerOutbound",
|
||||
"properties": {
|
||||
"description": "allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "AzureLoadBalancer",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DenyVnetOutBound",
|
||||
"properties": {
|
||||
"description": "isolate instances on the same range from each another",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Deny",
|
||||
"priority": 3996,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.1
|
||||
parameters:
|
||||
location:
|
||||
type: string
|
||||
defaultValue: UK South
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFWindows
|
||||
metadata:
|
||||
description: Name of the network security group
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[parameters('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: RDP
|
||||
properties:
|
||||
description: Allow RDP traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 3389
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerInBound
|
||||
properties:
|
||||
description: allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'AzureLoadBalancer'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Inbound
|
||||
- name: DenyVnetInBound
|
||||
properties:
|
||||
description: isolate instances on the same range from each another
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Deny
|
||||
priority: 3996
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerOutbound
|
||||
properties:
|
||||
description: allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'AzureLoadBalancer'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Outbound
|
||||
- name: DenyVnetOutBound
|
||||
properties:
|
||||
description: isolate instances on the same range from each another
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Deny
|
||||
priority: 3996
|
||||
direction: Outbound
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
## Default target Resource Group
|
||||
|
||||
These templates were built on the GBUoN-uks-Dev subscription in the UI-SPP-DEV-001 resource group.
|
||||
|
||||
## json vs yaml
|
||||
The templates are written in yaml and converted to json with yarn. Conversion operates both ways, it is helpful to take example json arm templates and convert to yaml - usage
|
||||
https://github.com/Azure/azure-quickstart-templates Yaml allows comments and is much easier to read. https://github.com/TeamYARM/YARM-CLI
|
||||
|
||||
./Yarm.ConsoleApp.exe -i spectrum_scale_ARM_v2.yaml
|
||||
spectrum_scale_ARM_v2.yaml => spectrum_scale_ARM_v2.json
|
||||
|
||||
## Templates
|
||||
CFInstance.json
|
||||
CFInstance.yaml
|
||||
CFLinux_nsg.json
|
||||
CFLinux_nsg.yaml
|
||||
CFWindows_nsg.json
|
||||
CFWindows_nsg.yaml
|
||||
spectrum_scale_ARM_v2.yaml
|
||||
|
||||
## Purpose
|
||||
|
||||
### CFinstance
|
||||
Generic template for an Azure instance with attached network adapter and public ip, will use parameterized network security group.
|
||||
|
||||
### CFWindows/CFLinux
|
||||
Templates for windows or linux network security group, allows RDP/SSH respectively. Host isolation rules in place.
|
||||
|
||||
### spectrum_scale_ARM_v2
|
||||
Template for single node spectrum scale storage, includes own network security group and secondary IP (previously secondary network interface). Used to write Ansible API calls.
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "RedHat:RHEL:7.7:7.7.2020020415",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"defaultValue": "ocfadmin",
|
||||
"metadata": {
|
||||
"description": "User name for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"defaultValue": "mnBMghZLWg63Kge2",
|
||||
"metadata": {
|
||||
"description": "Password for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B4ms",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "UI-SPP-DEV-001-vnet",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "default",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "UK South",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"publicIPAddressName": "[concat(variables('resourcePrefix'), '-pip')]",
|
||||
"publicIPAddressType": "Dynamic",
|
||||
"publicIpAddressSku": "Basic",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupName": "[variables('resourcePrefix')]",
|
||||
"subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/publicIPAddresses",
|
||||
"name": "[variables('publicIPAddressName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"publicIPAddressVersion": "IPv4",
|
||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
|
||||
"idleTimeoutInMinutes": 4
|
||||
},
|
||||
"sku": {
|
||||
"name": "[variables('publicIpAddressSku')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[variables('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "SSH",
|
||||
"properties": {
|
||||
"description": "Allow SSH traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 22,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
|
||||
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"primary": true,
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"publicIPAddress": {
|
||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
},
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ipconfig2",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
|
||||
},
|
||||
"enableIPForwarding": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "ucats",
|
||||
"OwnerMail": "ucats@nottingham.ac.uk",
|
||||
"CFRequestID": "spectrum_scale_test",
|
||||
"CFServiceID": "spectrum_scale_test",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: RedHat:RHEL:7.7:7.7.2020020415
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table
|
||||
adminUsername:
|
||||
type: string
|
||||
defaultValue: ocfadmin
|
||||
metadata:
|
||||
description: User name for the Virtual Machine.
|
||||
adminPassword:
|
||||
type: securestring
|
||||
defaultValue: mnBMghZLWg63Kge2
|
||||
metadata:
|
||||
description: Password for the Virtual Machine.
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B4ms
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: UI-SPP-DEV-001-vnet
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: default
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: UK South
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
variables:
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
publicIPAddressName: "[concat(variables('resourcePrefix'), '-pip')]"
|
||||
publicIPAddressType: Dynamic
|
||||
publicIpAddressSku: Basic
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupName: "[variables('resourcePrefix')]"
|
||||
subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]"
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/publicIPAddresses
|
||||
name: "[variables('publicIPAddressName')]"
|
||||
location: "[parameters('location')]"
|
||||
properties:
|
||||
publicIPAddressVersion: IPv4
|
||||
publicIPAllocationMethod: "[variables('publicIPAddressType')]"
|
||||
idleTimeoutInMinutes: 4
|
||||
sku:
|
||||
name: "[variables('publicIpAddressSku')]"
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[variables('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: SSH
|
||||
properties:
|
||||
description: Allow SSH traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 22
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
- "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
primary: true
|
||||
privateIPAllocationMethod: Dynamic
|
||||
publicIPAddress:
|
||||
id: "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
subnet:
|
||||
id: "[variables('subnetRef')]"
|
||||
# this only allocates an ip in Azure, waagent not smart enough to bind to adapter
|
||||
# used to reserve a floating ip that can be used for CES, enableIPForwarding: boolean required
|
||||
- name: ipconfig2
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
id: "[variables('subnetRef')]"
|
||||
networkSecurityGroup:
|
||||
id: "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
|
||||
enableIPForwarding: true
|
||||
# no longer using secondary interface for CES, using above reserved ip for ces
|
||||
# - apiVersion: 2019-11-01
|
||||
# type: Microsoft.Network/networkInterfaces
|
||||
# name: "[concat(variables('nicName'), '1')]"
|
||||
# location: "[parameters('location')]"
|
||||
# dependsOn:
|
||||
# - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
# - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
|
||||
# properties:
|
||||
# ipConfigurations:
|
||||
# - name: ipconfig1
|
||||
# properties:
|
||||
# privateIPAllocationMethod: Dynamic
|
||||
# subnet:
|
||||
# id: "[variables('subnetRef')]"
|
||||
# networkSecurityGroup:
|
||||
# id: "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: ucats
|
||||
OwnerMail: ucats@nottingham.ac.uk
|
||||
CFRequestID: spectrum_scale_test
|
||||
CFServiceID: spectrum_scale_test
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
# no longer using secondary interface for CES
|
||||
# - id: "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicName'), '1'))]"
|
||||
# properties:
|
||||
# primary: false
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "RedHat:RHEL:7.8:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "User name for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Azure.Extensions",
|
||||
"type": "CustomScript",
|
||||
"typeHandlerVersion": 2.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"skipDos2Unix": false
|
||||
},
|
||||
"protectedSettings": {
|
||||
"script": "H4sIAGczD18CA61XbXPbNhL+zl+xlZ2xPRFI2ZMmEzu6q8exm06duNM66fRyHg5EgBIsEGAAUIqT9n777QKUZDu6m/twXyhxAezLg91nlzvfFRNlign3syzbAR9sC97PIFiQxndOgrHQeelA26kyIDqnzBSEbTi+3Vp8PIWuFTzIIQjl+URLCDPlobYOhJx008zf+SCbKui1ekG2ljNpUDWp04sGVMOnEhgZA6418AVXOqrzLa8koKUPPwI3AuTnIPFnZhsJlx8yvegFTMPhaPTk4tfzcyiEXBTO2rCYFrRRL7LPtS+nzi6BiW3LFL0MEL5kQTWSAooey8Do/Ys1Es47Z1tZXFojrKEDMf6ERXbXNcDu0E0fyH0nuW4EBusFzN3ke7a0bo5LQVmTBFpNPFghbu2k/2HNnJwRyoHnzYSzyjaNNSxYqz1wUWmVZRUP8DcoZKgKUpNX1tTw6tX51QU6dIYvatq5ZMYb1bYyeGj4HUwktBqBFIRkvCG0gzFadwfcw1JqnSlT6U5EBx4ayEWRZR8xBTAHpjcZXmzNOx1gDBc/XZ4fFwvuClyNByiuHF8ymItq6w6Upw1cNMqUmF0LTLBvdsZVEXeibTXpjfqN/TKijCdPX+fvrq6vf3r345vTt/npWf7+Z9xkfKmtnXftel9wnXywkFxM4qCquQylVrWkK0f50bNZhjdp5PK+9IXIKLuX3ImYn6vzDvXiS821x7d2rowKJTfVzDq/jo5gxaUiaF9U0gV8cjbpjNAyr1zIYGcVW1XxaiZLw6PRn8//+BXDO26l8wrryYTjJ187Jf5CbGJ0BMsWGPDo19VNbEXp0R1s3UNGUponJG/oWG5sCJgOM97kvMq7+X86nf/veymLM4mIwd6pPxy9bN/88ex39dK+MPwfZuZ2vtuDPyECC+wDkNOqkuWZtp24sK7xP2xTeq9iUlXer5mPvApqIdm6GG6y/gIYlhvCjJ5SESMCvbmbjHcBwcB0YatqH8Od9LhlS6Q3WcMNUhtLNIhbjb2vQbCGty2xYFKCNd92QTrWX/zu/sz6EF+YP9gs2w4XbTc+/YIsvUGg/OXqbIjia+nJjaGoxlzQc+NXlFX07OYJ8Q1DOzzGXejpK0t1E2luG7LIOJ86iQTSyMCRMjkEPqXeUWPhgl0a6R7z4iyE1h8XhdB5LYV1vHX2FpHPrZsWbTcpZCt1fDAnteReMo1c7AN7gdByV81y1zaPtd5+yqK18e5+1TmNSL2Bt71Px1SdMCDDaPfw+cv86Ptnef9brDwvoipTyb/zVrEFVZk146PR4Us2es5GhwNMvNtPkPf45xioR5FHPt3zxT8HRTGl3OTLObALgMHJAPa+Ikvsq/HhiXo1fndxop4+PVD1/q76V3FFzh4XB9jbtAooGiLwmHPDwfHg4KTFHktXQJKPRzfw195Bpmr4COwLDHZjoAO4OUEWl6aPu1GeOil6ldUPmwQlb3w8yHoSYKbHmiZy2pK4WRWbSVkrLcseEOLErK8DOma8H0LLmzU/FFsrgIuynxi2W6Km8F+5PKVjGUEfx3yUgi1VmLHUFRNVVk4KrFiFBIy7romUlSgxwRZKRHLjIpnCOcTJsuXeY1cWpapLW9cala6OrTjYz2Ss7s2QpAVvS8+9LrGGZ0p8U6G7aQsa3tR11Il9QU94NS9XPR7V0t/iSffDE0GUMHvk6w501MVbWalaVUDxwqr+gVeV7TBN7ITKJy3irIY9KMMRqqw7re/KTx3HxqWkiG0kdqHYnaTpGolTglxLki3NHQ5hm8EAuw3mcEN1AbalocJnamoIPRylsH82splIt4Yb0Xd3ZboN6pa2I/58PhqtYpFUwYBI4pTYGUKHIWeg0QBX7+PM+MiFhCZNoGU6XMbDY9w+fo9SP6Q/RsUEDXfD12fEd/i8x3ckq+iJqbYT9SXn/y8KV/6Vwc6liYr9PVSJUJ3CC8L7kt4TOSZeTO/3b9yrptUySz8lZotdxsBJXar6xNbrMR1HS8wApfsBnFhXYcaKbyduf18mTRqrY1eLU2+Hezph0dQD5kginP02off8kdyB08vL8T4+DuDd1S+nv/32++tjkvVurshZSD8nN2pnGyBmR1BaO1z54bDG+uTiaan/dmhxqJemUtgUke5ZoiOWuqkDxtbnO8VICXvB0hjDomilkmG/8I8bxuejqU2bVyvx5lbLb0+vz7P+9qgdrikvepaobPNVtKV/PoJ8I47fPrFnp+8YYlUcxS1WslZz/PTBvRP8MBkmWX+zVN1US6vVh99n/WSBBlSN44zPdvqY0ulsJx1Cw/IzTk5Th58BkR9iOEuOeJqQxbXRvwHIUlJJEQ4AAA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"value": "[variables('owner')]"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"value": "[parameters('email')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: RedHat:RHEL:7.8:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: User name for the Virtual Machine.
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the Virtual Machine.
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
#defaultValue: unused
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
publisher: Microsoft.Azure.Extensions
|
||||
type: CustomScript
|
||||
typeHandlerVersion: 2.1
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
skipDos2Unix: false
|
||||
protectedSettings:
|
||||
script: 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
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
owner:
|
||||
type: string
|
||||
value: "[variables('owner')]"
|
||||
email:
|
||||
type: string
|
||||
value: "[parameters('email')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
ipAddress:
|
||||
type: string
|
||||
#value: "[reference('nicName').ipConfigurations[0].properties.privateIPAddress]"
|
||||
#value: "[reference(concat(variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "Canonical:UbuntuServer:18.04-LTS:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "User name for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Azure.Extensions",
|
||||
"type": "CustomScript",
|
||||
"typeHandlerVersion": 2.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"skipDos2Unix": false
|
||||
},
|
||||
"protectedSettings": {
|
||||
"script": "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"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"value": "[variables('owner')]"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"value": "[parameters('email')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: Canonical:UbuntuServer:18.04-LTS:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: User name for the Virtual Machine.
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the Virtual Machine.
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
#defaultValue: unused
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
publisher: Microsoft.Azure.Extensions
|
||||
type: CustomScript
|
||||
typeHandlerVersion: 2.1
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
skipDos2Unix: false
|
||||
protectedSettings:
|
||||
script: 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
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
owner:
|
||||
type: string
|
||||
value: "[variables('owner')]"
|
||||
email:
|
||||
type: string
|
||||
value: "[parameters('email')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
ipAddress:
|
||||
type: string
|
||||
#value: "[reference('nicName').ipConfigurations[0].properties.privateIPAddress]"
|
||||
#value: "[reference(concat(variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "User name for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the Virtual Machine."
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFWindows",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region"
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"domainToJoin": {
|
||||
"type": "string",
|
||||
"defaultValue": "ad.nottingham.ac.uk",
|
||||
"metadata": {
|
||||
"description": "The FQDN of the AD domain"
|
||||
}
|
||||
},
|
||||
"domainUsername": {
|
||||
"type": "string",
|
||||
"defaultValue": "service_CloudForms",
|
||||
"metadata": {
|
||||
"description": "Username of the account on the domain"
|
||||
}
|
||||
},
|
||||
"domainPassword": {
|
||||
"type": "securestring",
|
||||
"defaultValue": "As109pHY4Wi9o7naZnhr#!",
|
||||
"metadata": {
|
||||
"description": "Password of the account on the domain"
|
||||
}
|
||||
},
|
||||
"ouPath": {
|
||||
"type": "string",
|
||||
"defaultValue": "OU=AzureCloudForms_POC,OU=Testing,DC=ad,DC=nottingham,DC=ac,DC=uk",
|
||||
"metadata": {
|
||||
"description": "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"
|
||||
}
|
||||
},
|
||||
"domainJoinOptions": {
|
||||
"type": "int",
|
||||
"defaultValue": 3,
|
||||
"metadata": {
|
||||
"description": "Set of bit flags that define the join options. Default value of 3 is a combination of NETSETUP_JOIN_DOMAIN (0x00000001) & NETSETUP_ACCT_CREATE (0x00000002) i.e. will join the domain and create the account on the domain. For more information see https://msdn.microsoft.com/en-us/library/aa392154(v=vs.85).aspx"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"rdpgroupsCmd": "[concat('Add-LocalGroupMember -Group ''Remote Desktop Users'' -Member', ' ', variables('owner'))]",
|
||||
"localadminCommand": "[concat('Add-LocalGroupMember -Group Administrators -Member', ' ', variables('owner'))]",
|
||||
"powershellCmd": "[concat('powershell.exe -ExecutionPolicy Unrestricted', ' ', variables('rdpgroupsCmd'), ';', variables('localadminCommand'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'joindomain')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"tags": {
|
||||
"domain": "[parameters('domainToJoin')]"
|
||||
},
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Compute",
|
||||
"type": "JsonADDomainExtension",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"Name": "[parameters('domainToJoin')]",
|
||||
"OUPath": "[parameters('ouPath')]",
|
||||
"User": "[concat(parameters('domainUsername'), '@', parameters('domainToJoin'))]",
|
||||
"Restart": true,
|
||||
"Options": "[parameters('domainJoinOptions')]"
|
||||
},
|
||||
"protectedSettings": {
|
||||
"Password": "[parameters('domainPassword')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-06-01",
|
||||
"condition": "[not(or(equals(variables('owner'), 'unused'), equals(variables('owner'), 'donotreply')))]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/rdpgroups')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/joindomain')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Compute",
|
||||
"type": "CustomScriptExtension",
|
||||
"typeHandlerVersion": 1.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": "",
|
||||
"protectedSettings": {
|
||||
"commandToexecute": "[variables('powershellCmd')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: "MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest"
|
||||
metadata:
|
||||
description: "az vm image list --output table / az vm image list -p RedHat --all --output table"
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: User name for the Virtual Machine.
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the Virtual Machine.
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFWindows
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: "[utcNow()]"
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
domainToJoin:
|
||||
type: string
|
||||
defaultValue: ad.nottingham.ac.uk
|
||||
metadata:
|
||||
description: The FQDN of the AD domain
|
||||
domainUsername:
|
||||
type: string
|
||||
defaultValue: service_CloudForms
|
||||
metadata:
|
||||
description: Username of the account on the domain
|
||||
domainPassword:
|
||||
type: securestring
|
||||
defaultValue: As109pHY4Wi9o7naZnhr#!
|
||||
metadata:
|
||||
description: Password of the account on the domain
|
||||
ouPath:
|
||||
type: string
|
||||
defaultValue: OU=AzureCloudForms_POC,OU=Testing,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
metadata:
|
||||
description: "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"
|
||||
domainJoinOptions:
|
||||
type: int
|
||||
defaultValue: 3
|
||||
metadata:
|
||||
description: Set of bit flags that define the join options. Default value of 3 is a combination of NETSETUP_JOIN_DOMAIN (0x00000001) & NETSETUP_ACCT_CREATE (0x00000002) i.e. will join the domain and create the account on the domain. For more information see https://msdn.microsoft.com/en-us/library/aa392154(v=vs.85).aspx
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
rdpgroupsCmd: "[concat('Add-LocalGroupMember -Group ''Remote Desktop Users'' -Member', ' ', variables('owner'))]"
|
||||
localadminCommand: "[concat('Add-LocalGroupMember -Group Administrators -Member', ' ', variables('owner'))]"
|
||||
powershellCmd: "[concat('powershell.exe -ExecutionPolicy Unrestricted', ' ', variables('rdpgroupsCmd'), ';', variables('localadminCommand'))]"
|
||||
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2015-06-15
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'joindomain')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
tags:
|
||||
domain: "[parameters('domainToJoin')]"
|
||||
properties:
|
||||
publisher: Microsoft.Compute
|
||||
type: JsonADDomainExtension
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
Name: "[parameters('domainToJoin')]"
|
||||
OUPath: "[parameters('ouPath')]"
|
||||
User: "[concat(parameters('domainUsername'), '@', parameters('domainToJoin'))]"
|
||||
Restart: true
|
||||
Options: "[parameters('domainJoinOptions')]"
|
||||
protectedSettings:
|
||||
Password: "[parameters('domainPassword')]"
|
||||
- apiVersion: 2018-06-01
|
||||
# conditionally run if owner is a valid domain user and not a placeholder from this template or cloudforms admin user without valid uon email/account
|
||||
# condition: "[or(not(equals(variables('owner'), 'unused')), not(equals(variables('owner'), 'donotreply')))]" # wont match second condition
|
||||
condition: "[not(or(equals(variables('owner'), 'unused'), equals(variables('owner'), 'donotreply')))]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/rdpgroups')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/joindomain')]"
|
||||
properties:
|
||||
publisher: Microsoft.Compute
|
||||
type: CustomScriptExtension
|
||||
typeHandlerVersion: 1.10
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
protectedSettings:
|
||||
commandToexecute: "[variables('powershellCmd')]"
|
||||
# example for adding host entry when using default Azure dns
|
||||
# commandToExecute: powershell.exe -ExecutionPolicy Unrestricted Add-Content -Path "$env:windir\System32\drivers\etc\hosts" -Value "`r`n10.102.1.6`tad.nottingham.ac.uk" -Force
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: "[deployment().name]"
|
||||
ipAddress:
|
||||
type: string
|
||||
#value: "[reference('nicName').ipConfigurations[0].properties.privateIPAddress]"
|
||||
#value: "[reference(concat(variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "Name of the network security group"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "SSH",
|
||||
"properties": {
|
||||
"description": "Allow SSH traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 22,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerInBound",
|
||||
"properties": {
|
||||
"description": "allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "AzureLoadBalancer",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerOutbound",
|
||||
"properties": {
|
||||
"description": "allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "AzureLoadBalancer",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.1
|
||||
parameters:
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: Name of the network security group
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[parameters('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: SSH
|
||||
properties:
|
||||
description: Allow SSH traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 22
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerInBound
|
||||
properties:
|
||||
description: allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'AzureLoadBalancer'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Inbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetInBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Inbound
|
||||
- name: AllowAzureLoadBalancerOutbound
|
||||
properties:
|
||||
description: allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'AzureLoadBalancer'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Outbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetOutBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Outbound
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFWindows",
|
||||
"metadata": {
|
||||
"description": "Name of the network security group"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "RDP",
|
||||
"properties": {
|
||||
"description": "Allow RDP traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 3389,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerInBound",
|
||||
"properties": {
|
||||
"description": "allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "AzureLoadBalancer",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerOutbound",
|
||||
"properties": {
|
||||
"description": "allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "AzureLoadBalancer",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.1
|
||||
parameters:
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFWindows
|
||||
metadata:
|
||||
description: Name of the network security group
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[parameters('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: RDP
|
||||
properties:
|
||||
description: Allow RDP traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 3389
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerInBound
|
||||
properties:
|
||||
description: allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'AzureLoadBalancer'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Inbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetInBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Inbound
|
||||
- name: AllowAzureLoadBalancerOutbound
|
||||
properties:
|
||||
description: allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'AzureLoadBalancer'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Outbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetOutBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Outbound
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
## Default target Resource Group
|
||||
|
||||
These templates were built on the Research Managed subscription in the rg-svc-rem-we-spp-1 resource group, the network vnet+subnet used resuide within the rg-vn-rem-we-1 resource group.
|
||||
|
||||
## json vs yaml
|
||||
The templates are written in yaml and converted to json with yarn. Conversion operates both ways, it is helpful to take example json arm templates and convert to yaml - usage
|
||||
https://github.com/Azure/azure-quickstart-templates Yaml allows comments and is much easier to read. https://github.com/TeamYARM/YARM-CLI
|
||||
|
||||
./Yarm.ConsoleApp.exe -i CFInstance_win.yaml
|
||||
CFInstance_win.yaml => CFInstance_win.json
|
||||
|
||||
## Templates
|
||||
CFInstance_rhel.json
|
||||
CFInstance_rhel.yaml
|
||||
CFInstance_win.json
|
||||
CFInstance_win.yaml
|
||||
CFLinux_nsg.json
|
||||
CFLinux_nsg.yaml
|
||||
CFWindows_nsg.json
|
||||
CFWindows_nsg.yaml
|
||||
rhel_customscript_extension.sh
|
||||
|
||||
## Purpose
|
||||
|
||||
### Azure_RHEL_instance / Azure_UbuntuServer_instance
|
||||
Template for a RHEL Azure instance with attached network adapter and dynamic private ip, uses parameterized network security group CFLinux_nsg.
|
||||
Parameter networkResourceGroup included for UoN midtier vnet that resides in a different resource group, if the value is 'unused' network interfaces will be build in the vnet+subnet of the resource group supplied during the invocation of the template.
|
||||
Uses waagent to run a CustomScript extension rather than cloud_init, this joins the host to the domain and performs a lookup of the owner tag to modify the sssd.conf with the tag value to ensure only the owner has access to the instance.
|
||||
The customscript is base64 encoded and the resultant string is put in the ARM template.
|
||||
|
||||
#### create customscript extension script property
|
||||
|
||||
https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-linux
|
||||
|
||||
cat rhel_customscript_extension.sh | gzip -9 | base64 -w 0
|
||||
|
||||
#### customscript extensions
|
||||
|
||||
rhel_customscript_extension.sh
|
||||
ubuntu_customscript_extension.sh
|
||||
|
||||
### Azure_WindowsServer_instance
|
||||
|
||||
Template for a Windows Azure instance with attached network adapter and dynamic private ip, uses parameterized network security group CFWindows_nsg.
|
||||
Parameter networkResourceGroup included for UoN midtier vnet that resides in a different resource group, if the value is 'unused' network interfaces will be build in the vnet+subnet of the resource group supplied during the invocation of the template.
|
||||
Uses waagent to run JsonADDomainExtension extension and CustomScriptExtension extension to join a domain and chnage the local rdp group to ensure only the owner has access to the instance.
|
||||
|
||||
### CFLinux_Azure_network_security_group / CFWindows_Azure_network_security_group
|
||||
Templates for windows or linux network security group, allows RDP/SSH respectively. Host isolation rules dropped owing to gateway being in the default vnet range.
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
#!/bin/bash
|
||||
|
||||
# stop ssh to ensure no user login during domain join + update, disable this for debug
|
||||
systemctl stop sshd
|
||||
|
||||
# when using lvm image - use all available space in VG and extend home LV
|
||||
lvextend -l 100%FREE /dev/rootvg/homelv
|
||||
xfs_grow -d /dev/rootvg/homelv
|
||||
|
||||
# set tz
|
||||
timedatectl set-timezone Europe/London
|
||||
|
||||
# join domain
|
||||
yum -y install realmd sssd krb5-workstation krb5-libs oddjob oddjob-mkhomedir samba-common-tools adcli
|
||||
|
||||
cat > /etc/krb5.conf <<EOF
|
||||
# Configuration snippets may be placed in this directory as well
|
||||
includedir /etc/krb5.conf.d/
|
||||
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = AD.NOTTINGHAM.AC.UK
|
||||
dns_lookup_realm = true
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
rdns = false
|
||||
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
|
||||
#default_ccache_name = KEYRING:persistent:%{uid}
|
||||
|
||||
[realms]
|
||||
AD.NOTTINGHAM.AC.UK = {
|
||||
kdc = AD.NOTTINGHAM.AC.UK
|
||||
admin_server = AD.NOTTINGHAM.AC.UK
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
.ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
EOF
|
||||
|
||||
echo 'As109pHY4Wi9o7naZnhr#!' | kinit -V service_CloudForms@AD.NOTTINGHAM.AC.UK
|
||||
cat > /etc/realmd.conf <<EOF
|
||||
[active-directory]
|
||||
default-client = sssd
|
||||
|
||||
[service]
|
||||
automatic-install = yes
|
||||
|
||||
[ad.nottingham.ac.uk]
|
||||
manage-system = no
|
||||
automatic-id-mapping = yes
|
||||
computer-name = $(hostname -s)
|
||||
computer-ou = ou=AzureCloudForms_POC,ou=Testing,dc=ad,dc=nottingham,dc=ac,dc=uk
|
||||
EOF
|
||||
|
||||
systemctl restart realmd
|
||||
realm join AD.NOTTINGHAM.AC.UK
|
||||
|
||||
# query metadata tag to find owner
|
||||
yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
|
||||
yum -y install jq
|
||||
owner=$(curl -sH Metadata:true "http://169.254.169.254/metadata/instance?api-version=2019-06-01" | jq .compute.tags | sed 's/\"//g' | awk -F ";" '{for(i=1;i<=NF;i++)if($i~/Owner:/) split($i,result,":");print result[2] }')
|
||||
if [ -z "$owner" ]; then
|
||||
owner=missingtag
|
||||
fi
|
||||
|
||||
cat > /etc/sssd/sssd.conf <<EOF
|
||||
[sssd]
|
||||
domains = ad.nottingham.ac.uk
|
||||
config_file_version = 2
|
||||
services = nss, pam
|
||||
|
||||
[domain/ad.nottingham.ac.uk]
|
||||
ad_domain = ad.nottingham.ac.uk
|
||||
krb5_realm = AD.NOTTINGHAM.AC.UK
|
||||
realmd_tags = joined-with-adcli
|
||||
cache_credentials = True
|
||||
id_provider = ad
|
||||
krb5_store_password_if_offline = True
|
||||
default_shell = /bin/bash
|
||||
ldap_sasl_authid = $(hostname -s)$
|
||||
ldap_id_mapping = True
|
||||
fallback_homedir = /home/%u@%d
|
||||
auth_provider = ad
|
||||
# uon specific with computer account object with no dns
|
||||
use_fully_qualified_names = False
|
||||
enumerate = False
|
||||
# uon large directory performance options
|
||||
ignore_group_members = True
|
||||
entry_cache_timeout = 600
|
||||
# uon search base tuning - target OU for large directory
|
||||
ldap_user_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
#ldap_group_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
ldap_use_tokengroups = False
|
||||
# restrict access to owner
|
||||
access_provider = simple
|
||||
simple_allow_users = $owner
|
||||
EOF
|
||||
|
||||
# stop sssd until update finished
|
||||
systemctl stop sssd
|
||||
systemctl enable sssd
|
||||
|
||||
# setup sudoers
|
||||
cat > /etc/sudoers.d/nottingham <<EOF
|
||||
$owner ALL=(ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
#install desktop from epel repo, enable rhel optional repo for dependencies
|
||||
yum-config-manager --enable rhui-rhel-7-server-rhui-optional-rpms
|
||||
yum -y install x2goserver
|
||||
yum -y groupinstall MATE
|
||||
|
||||
# restart services for domain user login
|
||||
systemctl restart sssd
|
||||
systemctl restart sshd
|
||||
|
||||
#Azure extensions dont like a reboot, dont update without a reboot to ensure no system artifacts
|
||||
#yum -y update
|
||||
#reboot
|
||||
|
||||
#exit gracefully for waagent
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ubuntu much prefers cloud-init
|
||||
# should be rewritten with a common function to wait for apt db and metadata availability with timeouts
|
||||
|
||||
# stop ssh to ensure no user login during domain join + update, disable this for debug
|
||||
systemctl stop sshd
|
||||
|
||||
# set tz
|
||||
timedatectl set-timezone Europe/London
|
||||
|
||||
# join domain
|
||||
# wait loop until apt database is not locked to avoid clash with Azure policy installs, add a little delay to be able to contact apt repos (probably not Azure ones?)
|
||||
aptupdate=1
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
until [ $aptupdate -eq 0 ]
|
||||
do
|
||||
apt-get -y update
|
||||
apt-get -y install jq
|
||||
which jq
|
||||
aptupdate=$?
|
||||
sleep 5
|
||||
done
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y install krb5-user samba sssd sssd-tools libnss-sss libpam-sss realmd adcli expect
|
||||
|
||||
cat > /etc/krb5.conf <<EOF
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = AD.NOTTINGHAM.AC.UK
|
||||
dns_lookup_realm = true
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
rdns = false
|
||||
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
|
||||
#default_ccache_name = KEYRING:persistent:%{uid}
|
||||
|
||||
[realms]
|
||||
AD.NOTTINGHAM.AC.UK = {
|
||||
kdc = AD.NOTTINGHAM.AC.UK
|
||||
admin_server = AD.NOTTINGHAM.AC.UK
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
.ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
EOF
|
||||
|
||||
# password cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# if the password changes this script must be reprocessed and the ARM template updated in the CloudForms orchestration template
|
||||
echo 'As109pHY4Wi9o7naZnhr#!' | kinit -V service_CloudForms@AD.NOTTINGHAM.AC.UK
|
||||
cat > /etc/realmd.conf <<EOF
|
||||
[active-directory]
|
||||
default-client = sssd
|
||||
|
||||
[service]
|
||||
automatic-install = yes
|
||||
|
||||
[ad.nottingham.ac.uk]
|
||||
manage-system = no
|
||||
automatic-id-mapping = yes
|
||||
computer-name = $(hostname -s)
|
||||
computer-ou = ou=AzureCloudForms_POC,ou=Testing,dc=ad,dc=nottingham,dc=ac,dc=uk
|
||||
EOF
|
||||
|
||||
systemctl restart realmd
|
||||
realm join AD.NOTTINGHAM.AC.UK --install=/
|
||||
|
||||
# owner cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# query metadata tag to find owner, this will be used in the sssd.conf whitelist and sudoers
|
||||
owner=$(curl -sH Metadata:true "http://169.254.169.254/metadata/instance?api-version=2019-06-01" | jq .compute.tags | sed 's/\"//g' | awk -F ";" '{for(i=1;i<=NF;i++)if($i~/Owner:/) split($i,result,":");print result[2] }')
|
||||
if [ -z "$owner" ]; then
|
||||
owner=missingtag
|
||||
fi
|
||||
|
||||
cat > /etc/sssd/sssd.conf <<EOF
|
||||
[sssd]
|
||||
domains = ad.nottingham.ac.uk
|
||||
config_file_version = 2
|
||||
services = nss, pam
|
||||
|
||||
[domain/ad.nottingham.ac.uk]
|
||||
ad_domain = ad.nottingham.ac.uk
|
||||
krb5_realm = AD.NOTTINGHAM.AC.UK
|
||||
realmd_tags = joined-with-adcli
|
||||
cache_credentials = True
|
||||
id_provider = ad
|
||||
krb5_store_password_if_offline = True
|
||||
default_shell = /bin/bash
|
||||
ldap_sasl_authid = $(hostname -s)$
|
||||
ldap_id_mapping = True
|
||||
fallback_homedir = /home/%u@%d
|
||||
auth_provider = ad
|
||||
# uon specific with computer account object with no dns
|
||||
use_fully_qualified_names = False
|
||||
enumerate = False
|
||||
# uon large directory performance options
|
||||
ignore_group_members = True
|
||||
entry_cache_timeout = 600
|
||||
# uon search base tuning - target OU for large directory
|
||||
ldap_user_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
#ldap_group_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
ldap_use_tokengroups = False
|
||||
# restrict access to owner
|
||||
access_provider = simple
|
||||
simple_allow_users = $owner
|
||||
EOF
|
||||
|
||||
# stop sssd until update finished
|
||||
systemctl stop sssd
|
||||
systemctl enable sssd
|
||||
|
||||
# setup sudoers
|
||||
cat > /etc/sudoers.d/nottingham <<EOF
|
||||
$owner ALL=(ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
#install desktop, mate desktop meta package requires user interaction to select window manager, expect doesnt play well in whatever shell this script is run from - this minimal install is quicker @10mins
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y --no-install-recommends install x2goserver firefox caja compiz-mate engrampa eom folder-color-caja gnome-accessibility-themes gnome-colors-common gnome-icon-theme gnome-orca grub2-themes-ubuntu-mate indicator-messages indicator-power indicator-session indicator-sound lightdm-gtk-greeter mate-core mate-accessibility-profiles mate-applet-appmenu mate-applet-brisk-menu mate-calc mate-desktop mate-dock-applet mate-hud mate-icon-theme mate-menu mate-menus mate-netbook mate-optimus mate-screensaver mate-screensaver-common mate-system-monitor mate-tweak mate-user-guide mate-utils mate-window-applets-common mate-window-buttons-applet mate-window-menu-applet mate-window-title-applet plank plymouth-theme-ubuntu-mate-logo plymouth-theme-ubuntu-mate-text sessioninstaller sound-theme-freedesktop tilda ubuntu-mate-artwork ubuntu-mate-core ubuntu-mate-default-settings ubuntu-mate-guide ubuntu-mate-icon-themes ubuntu-mate-lightdm-theme ubuntu-mate-themes ubuntu-mate-wallpapers* ubuntu-standard
|
||||
|
||||
# enable home directory creation at logon - perform after desktop install to avoid manual prompts with desktop install
|
||||
sed -i 's/^.*pam_sss.so.*$/&\nsession required\tpam_mkhomedir.so skel=\/etc\/skel\/ umask=0077/' /etc/pam.d/common-session
|
||||
|
||||
# restart services for domain user login
|
||||
systemctl restart sssd
|
||||
systemctl restart sshd
|
||||
|
||||
#Azure extensions dont like a reboot, dont update without a reboot to ensure no system artifacts
|
||||
#apt-get -y upgrade
|
||||
#reboot
|
||||
|
||||
#exit gracefully for waagent
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "Canonical:UbuntuServer:18.04-LTS:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table, example Canonical:UbuntuServer:18.04-LTS:latest"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity"
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the local user account"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"defaultValue": "not classified",
|
||||
"metadata": {
|
||||
"description": "Uon Project Code"
|
||||
}
|
||||
},
|
||||
"toggleShutdownSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "f",
|
||||
"metadata": {
|
||||
"description": "t / f toggle for ComputeVmShutdownTask"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "UI-SPP-DEV-001-vnet",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "default",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "UK South",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"dataDiskType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"metadata": {
|
||||
"description": "storage account type for data disk"
|
||||
}
|
||||
},
|
||||
"dataDiskSizeGB": {
|
||||
"type": "string",
|
||||
"defaultValue": "128",
|
||||
"metadata": {
|
||||
"description": "data disk size in GB"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"publicIPAddressName": "[concat(variables('resourcePrefix'), '-pip')]",
|
||||
"publicIPAddressType": "Dynamic",
|
||||
"publicIpAddressSku": "Basic",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/publicIPAddresses",
|
||||
"name": "[variables('publicIPAddressName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"publicIPAddressVersion": "IPv4",
|
||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
|
||||
"idleTimeoutInMinutes": 4
|
||||
},
|
||||
"sku": {
|
||||
"name": "[variables('publicIpAddressSku')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"OS": "linux",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"publicIPAddress": {
|
||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
},
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"dataDisks": [
|
||||
{
|
||||
"diskSizeGB": "[parameters('dataDiskSizeGB')]",
|
||||
"lun": 0,
|
||||
"createOption": "Empty",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('dataDiskType')]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-09-15",
|
||||
"condition": "[equals(parameters('toggleShutdownSchedule'), 't')]",
|
||||
"type": "Microsoft.DevTestLab/schedules",
|
||||
"name": "[concat('shutdown-computevm-', variables('resourcePrefix'))]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"status": "Enabled",
|
||||
"taskType": "ComputeVmShutdownTask",
|
||||
"dailyRecurrence": {
|
||||
"time": 1900
|
||||
},
|
||||
"timeZoneId": "GMT Standard Time",
|
||||
"notificationSettings": {
|
||||
"status": "Enabled",
|
||||
"timeInMinutes": 15,
|
||||
"emailRecipient": "[parameters('email')]"
|
||||
},
|
||||
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"value": "[variables('owner')]"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"value": "[parameters('email')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: Canonical:UbuntuServer:18.04-LTS:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table, example Canonical:UbuntuServer:18.04-LTS:latest
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the local user account
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
projectCode:
|
||||
type: string
|
||||
defaultValue: not classified
|
||||
metadata:
|
||||
description: Uon Project Code
|
||||
toggleShutdownSchedule:
|
||||
type: string
|
||||
defaultValue: f
|
||||
metadata:
|
||||
description: t / f toggle for ComputeVmShutdownTask
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
#defaultValue: rg-vn-rem-we-1 # only used in the PROD resource group
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: UI-SPP-DEV-001-vnet
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: default
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: UK South
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
dataDiskType:
|
||||
type: string
|
||||
defaultValue: Standard_LRS
|
||||
metadata:
|
||||
description: storage account type for data disk
|
||||
dataDiskSizeGB:
|
||||
type: string
|
||||
defaultValue: "128"
|
||||
metadata:
|
||||
description: data disk size in GB
|
||||
# no access to domain in DEV resource group
|
||||
# domainToJoin:
|
||||
# type: string
|
||||
# defaultValue: ad.nottingham.ac.uk
|
||||
# metadata:
|
||||
# description: The FQDN of the AD domain
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
publicIPAddressName: "[concat(variables('resourcePrefix'), '-pip')]"
|
||||
publicIPAddressType: Dynamic
|
||||
publicIpAddressSku: Basic
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
resources:
|
||||
# public ip only available in DEV resource group
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/publicIPAddresses
|
||||
name: "[variables('publicIPAddressName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
publicIPAddressVersion: "IPv4"
|
||||
publicIPAllocationMethod: "[variables('publicIPAddressType')]"
|
||||
idleTimeoutInMinutes: 4
|
||||
sku:
|
||||
name: "[variables('publicIpAddressSku')]"
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
OS: linux
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
publicIPAddress:
|
||||
id: "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
dataDisks:
|
||||
- diskSizeGB: "[parameters('dataDiskSizeGB')]"
|
||||
lun: 0
|
||||
createOption: Empty
|
||||
managedDisk:
|
||||
storageAccountType: "[parameters('dataDiskType')]"
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
# no access to domain in DEV resource group
|
||||
# - apiVersion: 2019-12-01
|
||||
# type: Microsoft.Compute/virtualMachines/extensions
|
||||
# name: "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]"
|
||||
# location: "[parameters('location')]"
|
||||
# tags:
|
||||
# CFManaged: true
|
||||
# Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
# OwnerMail: "[parameters('email')]"
|
||||
# ADDomain: "[parameters('domainToJoin')]"
|
||||
# Cost Center: "[parameters('projectCode')]"
|
||||
# dependsOn:
|
||||
# - "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
# properties:
|
||||
# publisher: Microsoft.Azure.Extensions
|
||||
# type: CustomScript
|
||||
# typeHandlerVersion: 2.1
|
||||
# autoUpgradeMinorVersion: true
|
||||
# settings:
|
||||
# skipDos2Unix: false
|
||||
# protectedSettings:
|
||||
# script: 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
|
||||
- apiVersion: 2018-09-15
|
||||
condition: "[equals(parameters('toggleShutdownSchedule'), 't')]"
|
||||
type: Microsoft.DevTestLab/schedules
|
||||
name: "[concat('shutdown-computevm-', variables('resourcePrefix'))]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
status: Enabled
|
||||
taskType: ComputeVmShutdownTask
|
||||
dailyRecurrence:
|
||||
time: 1900
|
||||
timeZoneId: GMT Standard Time
|
||||
notificationSettings:
|
||||
status: Enabled
|
||||
timeInMinutes: 15
|
||||
emailRecipient: "[parameters('email')]"
|
||||
targetResourceId: "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
owner:
|
||||
type: string
|
||||
value: "[variables('owner')]"
|
||||
email:
|
||||
type: string
|
||||
value: "[parameters('email')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
ipAddress:
|
||||
type: string
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
#!/bin/bash
|
||||
|
||||
# stop ssh to ensure no user login during domain join + update, disable this for debug
|
||||
systemctl stop sshd
|
||||
|
||||
# when using lvm image - use all available space in VG and extend home LV
|
||||
lvextend -l 100%FREE /dev/rootvg/homelv
|
||||
xfs_grow -d /dev/rootvg/homelv
|
||||
|
||||
# add secondary disk
|
||||
parted -s /dev/sdc -- mklabel gpt mkpart primary xfs 0% 100%
|
||||
mkfs.xfs /dev/sdc1
|
||||
xfs_admin -L uondata /dev/sdc1
|
||||
mkdir /uondata
|
||||
echo "LABEL=uondata /uondata xfs defaults 0 0" >> /etc/fstab
|
||||
mount -a
|
||||
chmod 777 /uondata
|
||||
|
||||
# set tz
|
||||
timedatectl set-timezone Europe/London
|
||||
|
||||
# join domain
|
||||
yum -y install realmd sssd krb5-workstation krb5-libs oddjob oddjob-mkhomedir samba-common-tools adcli
|
||||
|
||||
cat > /etc/krb5.conf <<EOF
|
||||
# Configuration snippets may be placed in this directory as well
|
||||
includedir /etc/krb5.conf.d/
|
||||
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = AD.NOTTINGHAM.AC.UK
|
||||
dns_lookup_realm = true
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
rdns = false
|
||||
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
|
||||
#default_ccache_name = KEYRING:persistent:%{uid}
|
||||
|
||||
[realms]
|
||||
AD.NOTTINGHAM.AC.UK = {
|
||||
kdc = AD.NOTTINGHAM.AC.UK
|
||||
admin_server = AD.NOTTINGHAM.AC.UK
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
.ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
EOF
|
||||
|
||||
echo 'As109pHY4Wi9o7naZnhr#!' | kinit -V service_CloudForms@AD.NOTTINGHAM.AC.UK
|
||||
cat > /etc/realmd.conf <<EOF
|
||||
[active-directory]
|
||||
default-client = sssd
|
||||
|
||||
[service]
|
||||
automatic-install = yes
|
||||
|
||||
[ad.nottingham.ac.uk]
|
||||
manage-system = no
|
||||
automatic-id-mapping = yes
|
||||
computer-name = $(hostname -s)
|
||||
computer-ou = ou=AzureCloudForms_POC,ou=Testing,dc=ad,dc=nottingham,dc=ac,dc=uk
|
||||
EOF
|
||||
|
||||
systemctl restart realmd
|
||||
realm join AD.NOTTINGHAM.AC.UK
|
||||
|
||||
# query metadata tag to find owner
|
||||
yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
|
||||
yum -y install jq
|
||||
owner=$(curl -sH Metadata:true "http://169.254.169.254/metadata/instance?api-version=2019-06-01" | jq .compute.tags | sed 's/\"//g' | awk -F ";" '{for(i=1;i<=NF;i++)if($i~/Owner:/) split($i,result,":");print result[2] }')
|
||||
if [ -z "$owner" ]; then
|
||||
owner=missingtag
|
||||
fi
|
||||
|
||||
cat > /etc/sssd/sssd.conf <<EOF
|
||||
[sssd]
|
||||
domains = ad.nottingham.ac.uk
|
||||
config_file_version = 2
|
||||
services = nss, pam
|
||||
|
||||
[domain/ad.nottingham.ac.uk]
|
||||
ad_domain = ad.nottingham.ac.uk
|
||||
krb5_realm = AD.NOTTINGHAM.AC.UK
|
||||
realmd_tags = joined-with-adcli
|
||||
cache_credentials = True
|
||||
id_provider = ad
|
||||
krb5_store_password_if_offline = True
|
||||
default_shell = /bin/bash
|
||||
ldap_sasl_authid = $(hostname -s)$
|
||||
ldap_id_mapping = True
|
||||
fallback_homedir = /home/%u@%d
|
||||
auth_provider = ad
|
||||
# uon specific with computer account object with no dns
|
||||
use_fully_qualified_names = False
|
||||
enumerate = False
|
||||
# uon large directory performance options
|
||||
ignore_group_members = True
|
||||
entry_cache_timeout = 600
|
||||
# uon search base tuning - target OU for large directory
|
||||
ldap_user_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
#ldap_group_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
ldap_use_tokengroups = False
|
||||
# restrict access to owner
|
||||
access_provider = simple
|
||||
simple_allow_users = $owner
|
||||
EOF
|
||||
|
||||
# stop sssd until update finished
|
||||
systemctl stop sssd
|
||||
systemctl enable sssd
|
||||
|
||||
# setup sudoers
|
||||
cat > /etc/sudoers.d/nottingham <<EOF
|
||||
$owner ALL=(ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
#install desktop from epel repo, enable rhel optional repo for dependencies
|
||||
yum-config-manager --enable rhui-rhel-7-server-rhui-optional-rpms
|
||||
yum -y install x2goserver
|
||||
yum -y groupinstall MATE
|
||||
|
||||
# restart services for domain user login
|
||||
systemctl restart sssd
|
||||
systemctl restart sshd
|
||||
|
||||
#Azure extensions dont like a reboot, dont update without a reboot to ensure no system artifacts
|
||||
#yum -y update
|
||||
#reboot
|
||||
|
||||
#exit gracefully for waagent
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "RedHat:RHEL:7.7:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table, example RedHat:RHEL:7.7:latest"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity"
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the local user account"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"defaultValue": "not classified",
|
||||
"metadata": {
|
||||
"description": "Uon Project Code"
|
||||
}
|
||||
},
|
||||
"toggleShutdownSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "f",
|
||||
"metadata": {
|
||||
"description": "t / f toggle for ComputeVmShutdownTask"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"dataDiskType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"metadata": {
|
||||
"description": "storage account type for data disk"
|
||||
}
|
||||
},
|
||||
"dataDiskSizeGB": {
|
||||
"type": "string",
|
||||
"defaultValue": "128",
|
||||
"metadata": {
|
||||
"description": "data disk size in GB"
|
||||
}
|
||||
},
|
||||
"domainToJoin": {
|
||||
"type": "string",
|
||||
"defaultValue": "ad.nottingham.ac.uk",
|
||||
"metadata": {
|
||||
"description": "The FQDN of the AD domain"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"OS": "linux",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"dataDisks": [
|
||||
{
|
||||
"diskSizeGB": "[parameters('dataDiskSizeGB')]",
|
||||
"lun": 0,
|
||||
"createOption": "Empty",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('dataDiskType')]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Azure.Extensions",
|
||||
"type": "CustomScript",
|
||||
"typeHandlerVersion": 2.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"skipDos2Unix": false
|
||||
},
|
||||
"protectedSettings": {
|
||||
"script": "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"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-09-15",
|
||||
"condition": "[equals(parameters('toggleShutdownSchedule'), 't')]",
|
||||
"type": "Microsoft.DevTestLab/schedules",
|
||||
"name": "[concat('shutdown-computevm-', variables('resourcePrefix'))]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"status": "Enabled",
|
||||
"taskType": "ComputeVmShutdownTask",
|
||||
"dailyRecurrence": {
|
||||
"time": 1900
|
||||
},
|
||||
"timeZoneId": "GMT Standard Time",
|
||||
"notificationSettings": {
|
||||
"status": "Enabled",
|
||||
"timeInMinutes": 15,
|
||||
"emailRecipient": "[parameters('email')]"
|
||||
},
|
||||
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"condition": "[contains(parameters('vmSize'), 'Standard_N')]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/nvidia')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/join_domain_install_desktop')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.HpcCompute",
|
||||
"type": "NvidiaGpuDriverLinux",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"value": "[variables('owner')]"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"value": "[parameters('email')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: RedHat:RHEL:7.7:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table, example RedHat:RHEL:7.7:latest
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the local user account
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
projectCode:
|
||||
type: string
|
||||
defaultValue: not classified
|
||||
metadata:
|
||||
description: Uon Project Code
|
||||
toggleShutdownSchedule:
|
||||
type: string
|
||||
defaultValue: f
|
||||
metadata:
|
||||
description: t / f toggle for ComputeVmShutdownTask
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
#defaultValue: unused
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
dataDiskType:
|
||||
type: string
|
||||
defaultValue: Standard_LRS
|
||||
metadata:
|
||||
description: storage account type for data disk
|
||||
dataDiskSizeGB:
|
||||
type: string
|
||||
defaultValue: "128"
|
||||
metadata:
|
||||
description: data disk size in GB
|
||||
domainToJoin:
|
||||
type: string
|
||||
defaultValue: ad.nottingham.ac.uk
|
||||
metadata:
|
||||
description: The FQDN of the AD domain
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
OS: linux
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
dataDisks:
|
||||
- diskSizeGB: "[parameters('dataDiskSizeGB')]"
|
||||
lun: 0
|
||||
createOption: Empty
|
||||
managedDisk:
|
||||
storageAccountType: "[parameters('dataDiskType')]"
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
publisher: Microsoft.Azure.Extensions
|
||||
type: CustomScript
|
||||
typeHandlerVersion: 2.1
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
skipDos2Unix: false
|
||||
protectedSettings:
|
||||
script: H4sIAMsmD18CA61XX1McNxJ/n0/RWXABZbSzUI4pgzcXgiFOBZtUgp3K+agp7UizK1YjjSXNrrGTfPbrlmZ2Ae9d3cPxoGVaUv/vX7e2vsknyuQT7mdZtgU+2Aa8n0GwII1vnQRjofXSgbZTZUC0TpkpCFtz/Lq1uDyFthE8yH0QyvOJlhBmykNlHQg5aaeZv/NB1mXQK/aCZC1n0iBrYqcXNaiaTyUwEgZca+ALrnRk5xteSkBJ738EbgTIT0Hiz8zWEi7fZ3rREZiGg9HoycWv5+eQC7nInbVhMc3poF5knypfTJ1dAhObtlEjLgR4WVojuLsja+ZZw12QyNqnK16UwBjUc9RMapg2Af+nM9A4NABvoRQYPYmKZPW88kMi9HcPohJc1GgMu4SWJAV+b7ueC+Ug7zYyWc4sDC5Pfzi/HPeH6S+/90H8AT1d8VYHlA2jAXz3HeQylHnlA59ktW1NAMazclZbAUdHR2sJFHMZIHzOgqolhTHGSQZG35+tkXDeOtvI/BJvWEMXYtRTBmR3bQ3sDoODkjBoTnJdoxO9FzB3k2/Z0ro5bgVlTSJoNfFghbi1k+6H1XMKARnueT3hrLR1bQ0L1mqPMSm1yrKSB+iMIjZDDFIFL1+eX12gQmf4oaatS2K8UU0j0RU1v4OJhEZj+gjKn5iXKAdttBgq7mEptc6UKXUrogIPBQxFnmUfMPEx86c3We9kGMPFT5fnx/mCuxx34wWya4gfGcwxRzadQHo6EONfYE0tsKy+Ohl3RTyJstWkj+xafhG9jDdPXw3fXl1f//T2x9enb4anZ8N3P+Mh4wtt7bxtVueCa+WDjaRiIgdVzmUotKokhRzph89mGUbSyOV96pHIqKaX3IlYlf19h3zxo+La41czV0aFghvMXOdX1pFbcSsP2ueldAFXziatEVoOSxcy2OptK0tezmRheBT68/kfv6J5x410XiGKmHD85EurxF/om2gduWWDG/Dqlz4SG730KAYbz5CQlObJkzd0bWhsCJgOM14PeTls5//p9vB/P0tZnGp959QfjF40r/949rt6YY8M/6eZua1vduBPiI4F9h5IaVXK4kzbVlxYV/vvNzG9VzGpKu/XzAdeBrWQbFUMN1kXAIblhm5GTamI0QOduJuMtwGdgenC+mofw530eGSDpTdZzQ0COkvgj0eNvc9BsJo3DWF/YoI137RBOtYFfnt3Zn2IH8zvrbdti5u2HZ9+xt609kDxy9XZPpKvpSc19kU55oLWtV6RVtLazpPH133J4TUC8eSoLNVNhLlNnkXE+dhKBJBaBh5hOPApdcwKCxfs0kj3GBdnITT+OM+FHlZSWMcbZ2/R80PrpnnTTnLZSB0X5qSW3EumEYt9YEfoWu7K2dA19WOutx+zKG28vVu2TqOnXsObTqdjqk4YkGCUe/D8xfDw22fD7jfvNc8jK1PKf/BGsQVVmTXjw9HBCzZ6zkYHA0y8248w7Pw/REM9kjzi6Y7P/zXI8ynlJl/OgV0ADE4GsPMFUWJXjQ9O1Mvx24sT9fTpnqp2t9Xf+RUpe5zvYUfXKiBpHx2PObc/OB7snWAHNRQConw4vIG/dvYyVcEHYJ9hsB0NHcDNCaK4NJ3dtfI0P6BWWfWwSVDyxuVB1hMBMz3WNIHThsTNythMikppWXQOIUzMujqga8b7fWh4vcKHfGMFcFF0c9JmSdQU/iuWp3QsotPHMR+lYEsVZix1xQSVpZMCK1YhAOOpawJlJQpMsIUSEdy4SKJw+nKyaLj32JVFoarCVpVGpv21HoP9TMbqXo+GWvCm8NzrAmt4psRXFbqdjqDgdV1HntgX9ISX86Lv8ciW/s2ftN8/EQQJs0e6btFchCkiS1WpEshe6OsfeFnGacZOqHzSJk6o2IMyHByLqtX6rvjYcmxcSorYRmIXit1JmraWOCXIFSXJ0tzh6LkeDLDbYA7XVBdgGxoqfKamhryHAyT2z1rWE+lW7kbvu7siRYO6pW0JP5+PRr0tkioY0JM4G7eGvMMQM1BogKt3cVJ+pELyJs3dRbpcxMtjPD5+h1S/T/8YFRM03O2/OiO8w/Ue3hGtpBVTbSvyS8r/Xxj2+hXBzqWJjP09rxKgOoUBwnhJ7wkcEy6m7/sR96putMzST4HZYpfRcGKXqj6h9epxgqMlZoDS3bODUFdhxoqv3xn+Pk2a9JiIXS1OvS2eaYVFUQ+QI5Fw9lub3uFHUgdOLy/Hu7jswdurX05/++33V8dE69TswVlIPyc1KmdrIGRHpzR2v9fDYY11ycXTVvdiavApI02psCki3LMERyx1U4ePj9X9VjFiwo5YGmNYJPUsGfYL/7hhfDqc2nS434mR67ffnF6fZ130qB2uIC9qlqBs/Rbc0D8fuXxNji++2LPT641QFUdxi5Ws1RwffHh2gs+x/UTrIkvVTbXU7z58lXaTBQpQFY4zPtvqbEq3s610CQXLTzg5TR0+AyI+RHOWHP1pQhb3Rv8G1X6IhQcPAAA=
|
||||
- apiVersion: 2018-09-15
|
||||
condition: "[equals(parameters('toggleShutdownSchedule'), 't')]"
|
||||
type: Microsoft.DevTestLab/schedules
|
||||
name: "[concat('shutdown-computevm-', variables('resourcePrefix'))]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
status: Enabled
|
||||
taskType: ComputeVmShutdownTask
|
||||
dailyRecurrence:
|
||||
time: 1900
|
||||
timeZoneId: GMT Standard Time
|
||||
notificationSettings:
|
||||
status: Enabled
|
||||
timeInMinutes: 15
|
||||
emailRecipient: "[parameters('email')]"
|
||||
targetResourceId: "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
- apiVersion: 2015-06-15
|
||||
# supports up to Ubuntu 18.04 LTS and RHEL 7.7, will fight join_domain_install_desktop for apt lock without dependency
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/linux/n-series-driver-setup
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/hpccompute-gpu-linux
|
||||
condition: "[contains(parameters('vmSize'), 'Standard_N')]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/nvidia')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/join_domain_install_desktop')]"
|
||||
properties:
|
||||
publisher: Microsoft.HpcCompute
|
||||
type: NvidiaGpuDriverLinux
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings: {}
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
owner:
|
||||
type: string
|
||||
value: "[variables('owner')]"
|
||||
email:
|
||||
type: string
|
||||
value: "[parameters('email')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
ipAddress:
|
||||
type: string
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ubuntu much prefers cloud-init
|
||||
# should be rewritten with a common function to wait for apt db and metadata availability with timeouts
|
||||
|
||||
# stop ssh to ensure no user login during domain join + update, disable this for debug
|
||||
systemctl stop sshd
|
||||
|
||||
# add secondary disk
|
||||
parted -s /dev/sdc -- mklabel gpt mkpart primary xfs 0% 100%
|
||||
mkfs.xfs /dev/sdc1
|
||||
xfs_admin -L uondata /dev/sdc1
|
||||
mkdir /uondata
|
||||
echo "LABEL=uondata /uondata xfs defaults 0 0" >> /etc/fstab
|
||||
mount -a
|
||||
chmod 777 /uondata
|
||||
|
||||
# set tz
|
||||
timedatectl set-timezone Europe/London
|
||||
|
||||
# join domain
|
||||
# wait loop until apt database is not locked to avoid clash with Azure policy installs, add a little delay to be able to contact apt repos (probably not Azure ones?)
|
||||
aptupdate=1
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
until [ $aptupdate -eq 0 ]
|
||||
do
|
||||
apt-get -y update
|
||||
apt-get -y install jq
|
||||
which jq
|
||||
aptupdate=$?
|
||||
sleep 5
|
||||
done
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y install krb5-user samba sssd sssd-tools libnss-sss libpam-sss realmd adcli expect
|
||||
|
||||
cat > /etc/krb5.conf <<EOF
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = AD.NOTTINGHAM.AC.UK
|
||||
dns_lookup_realm = true
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
rdns = false
|
||||
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
|
||||
#default_ccache_name = KEYRING:persistent:%{uid}
|
||||
|
||||
[realms]
|
||||
AD.NOTTINGHAM.AC.UK = {
|
||||
kdc = AD.NOTTINGHAM.AC.UK
|
||||
admin_server = AD.NOTTINGHAM.AC.UK
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
.ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
EOF
|
||||
|
||||
# password cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# if the password changes this script must be reprocessed and the ARM template updated in the CloudForms orchestration template
|
||||
echo 'As109pHY4Wi9o7naZnhr#!' | kinit -V service_CloudForms@AD.NOTTINGHAM.AC.UK
|
||||
cat > /etc/realmd.conf <<EOF
|
||||
[active-directory]
|
||||
default-client = sssd
|
||||
|
||||
[service]
|
||||
automatic-install = yes
|
||||
|
||||
[ad.nottingham.ac.uk]
|
||||
manage-system = no
|
||||
automatic-id-mapping = yes
|
||||
computer-name = $(hostname -s)
|
||||
computer-ou = ou=AzureCloudForms_POC,ou=Testing,dc=ad,dc=nottingham,dc=ac,dc=uk
|
||||
EOF
|
||||
|
||||
systemctl restart realmd
|
||||
realm join AD.NOTTINGHAM.AC.UK --install=/
|
||||
|
||||
# owner cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# query metadata tag to find owner, this will be used in the sssd.conf whitelist and sudoers
|
||||
owner=$(curl -sH Metadata:true "http://169.254.169.254/metadata/instance?api-version=2019-06-01" | jq .compute.tags | sed 's/\"//g' | awk -F ";" '{for(i=1;i<=NF;i++)if($i~/Owner:/) split($i,result,":");print result[2] }')
|
||||
if [ -z "$owner" ]; then
|
||||
owner=missingtag
|
||||
fi
|
||||
|
||||
cat > /etc/sssd/sssd.conf <<EOF
|
||||
[sssd]
|
||||
domains = ad.nottingham.ac.uk
|
||||
config_file_version = 2
|
||||
services = nss, pam
|
||||
|
||||
[domain/ad.nottingham.ac.uk]
|
||||
ad_domain = ad.nottingham.ac.uk
|
||||
krb5_realm = AD.NOTTINGHAM.AC.UK
|
||||
realmd_tags = joined-with-adcli
|
||||
cache_credentials = True
|
||||
id_provider = ad
|
||||
krb5_store_password_if_offline = True
|
||||
default_shell = /bin/bash
|
||||
ldap_sasl_authid = $(hostname -s)$
|
||||
ldap_id_mapping = True
|
||||
fallback_homedir = /home/%u@%d
|
||||
auth_provider = ad
|
||||
# uon specific with computer account object with no dns
|
||||
use_fully_qualified_names = False
|
||||
enumerate = False
|
||||
# uon large directory performance options
|
||||
ignore_group_members = True
|
||||
entry_cache_timeout = 600
|
||||
# uon search base tuning - target OU for large directory
|
||||
ldap_user_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
#ldap_group_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
ldap_use_tokengroups = False
|
||||
# restrict access to owner
|
||||
access_provider = simple
|
||||
simple_allow_users = $owner
|
||||
EOF
|
||||
|
||||
# stop sssd until update finished
|
||||
systemctl stop sssd
|
||||
systemctl enable sssd
|
||||
|
||||
# setup sudoers
|
||||
cat > /etc/sudoers.d/nottingham <<EOF
|
||||
$owner ALL=(ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
#install desktop, mate desktop meta package requires user interaction to select window manager, expect doesnt play well in whatever shell this script is run from - this minimal install is quicker @10mins
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y --no-install-recommends install x2goserver firefox caja compiz-mate engrampa eom folder-color-caja gnome-accessibility-themes gnome-colors-common gnome-icon-theme gnome-orca grub2-themes-ubuntu-mate indicator-messages indicator-power indicator-session indicator-sound lightdm-gtk-greeter mate-core mate-accessibility-profiles mate-applet-appmenu mate-applet-brisk-menu mate-calc mate-desktop mate-dock-applet mate-hud mate-icon-theme mate-menu mate-menus mate-netbook mate-optimus mate-screensaver mate-screensaver-common mate-system-monitor mate-tweak mate-user-guide mate-utils mate-window-applets-common mate-window-buttons-applet mate-window-menu-applet mate-window-title-applet plank plymouth-theme-ubuntu-mate-logo plymouth-theme-ubuntu-mate-text sessioninstaller sound-theme-freedesktop tilda ubuntu-mate-artwork ubuntu-mate-core ubuntu-mate-default-settings ubuntu-mate-guide ubuntu-mate-icon-themes ubuntu-mate-lightdm-theme ubuntu-mate-themes ubuntu-mate-wallpapers* ubuntu-standard
|
||||
|
||||
# enable home directory creation at logon - perform after desktop install to avoid manual prompts with desktop install
|
||||
sed -i 's/^.*pam_sss.so.*$/&\nsession required\tpam_mkhomedir.so skel=\/etc\/skel\/ umask=0077/' /etc/pam.d/common-session
|
||||
|
||||
# restart services for domain user login
|
||||
systemctl restart sssd
|
||||
systemctl restart sshd
|
||||
|
||||
#Azure extensions dont like a reboot, dont update without a reboot to ensure no system artifacts
|
||||
#apt-get -y upgrade
|
||||
#reboot
|
||||
|
||||
#exit gracefully for waagent
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "Canonical:UbuntuServer:18.04-LTS:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table, example Canonical:UbuntuServer:18.04-LTS:latest"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity"
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the local user account"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"defaultValue": "not classified",
|
||||
"metadata": {
|
||||
"description": "Uon Project Code"
|
||||
}
|
||||
},
|
||||
"toggleShutdownSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "f",
|
||||
"metadata": {
|
||||
"description": "t / f toggle for ComputeVmShutdownTask"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"dataDiskType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"metadata": {
|
||||
"description": "storage account type for data disk"
|
||||
}
|
||||
},
|
||||
"dataDiskSizeGB": {
|
||||
"type": "string",
|
||||
"defaultValue": "128",
|
||||
"metadata": {
|
||||
"description": "data disk size in GB"
|
||||
}
|
||||
},
|
||||
"domainToJoin": {
|
||||
"type": "string",
|
||||
"defaultValue": "ad.nottingham.ac.uk",
|
||||
"metadata": {
|
||||
"description": "The FQDN of the AD domain"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"OS": "linux",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"dataDisks": [
|
||||
{
|
||||
"diskSizeGB": "[parameters('dataDiskSizeGB')]",
|
||||
"lun": 0,
|
||||
"createOption": "Empty",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('dataDiskType')]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Azure.Extensions",
|
||||
"type": "CustomScript",
|
||||
"typeHandlerVersion": 2.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"skipDos2Unix": false
|
||||
},
|
||||
"protectedSettings": {
|
||||
"script": "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"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-09-15",
|
||||
"condition": "[equals(parameters('toggleShutdownSchedule'), 't')]",
|
||||
"type": "Microsoft.DevTestLab/schedules",
|
||||
"name": "[concat('shutdown-computevm-', variables('resourcePrefix'))]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"status": "Enabled",
|
||||
"taskType": "ComputeVmShutdownTask",
|
||||
"dailyRecurrence": {
|
||||
"time": 1900
|
||||
},
|
||||
"timeZoneId": "GMT Standard Time",
|
||||
"notificationSettings": {
|
||||
"status": "Enabled",
|
||||
"timeInMinutes": 15,
|
||||
"emailRecipient": "[parameters('email')]"
|
||||
},
|
||||
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"condition": "[contains(parameters('vmSize'), 'Standard_N')]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/nvidia')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/join_domain_install_desktop')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.HpcCompute",
|
||||
"type": "NvidiaGpuDriverLinux",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"value": "[variables('owner')]"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"value": "[parameters('email')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: Canonical:UbuntuServer:18.04-LTS:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table, example Canonical:UbuntuServer:18.04-LTS:latest
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the local user account
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
projectCode:
|
||||
type: string
|
||||
defaultValue: not classified
|
||||
metadata:
|
||||
description: Uon Project Code
|
||||
toggleShutdownSchedule:
|
||||
type: string
|
||||
defaultValue: f
|
||||
metadata:
|
||||
description: t / f toggle for ComputeVmShutdownTask
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
#defaultValue: unused
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
dataDiskType:
|
||||
type: string
|
||||
defaultValue: Standard_LRS
|
||||
metadata:
|
||||
description: storage account type for data disk
|
||||
dataDiskSizeGB:
|
||||
type: string
|
||||
defaultValue: "128"
|
||||
metadata:
|
||||
description: data disk size in GB
|
||||
domainToJoin:
|
||||
type: string
|
||||
defaultValue: ad.nottingham.ac.uk
|
||||
metadata:
|
||||
description: The FQDN of the AD domain
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
OS: linux
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
dataDisks:
|
||||
- diskSizeGB: "[parameters('dataDiskSizeGB')]"
|
||||
lun: 0
|
||||
createOption: Empty
|
||||
managedDisk:
|
||||
storageAccountType: "[parameters('dataDiskType')]"
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'join_domain_install_desktop')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
publisher: Microsoft.Azure.Extensions
|
||||
type: CustomScript
|
||||
typeHandlerVersion: 2.1
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
skipDos2Unix: false
|
||||
protectedSettings:
|
||||
script: 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
|
||||
- apiVersion: 2018-09-15
|
||||
condition: "[equals(parameters('toggleShutdownSchedule'), 't')]"
|
||||
type: Microsoft.DevTestLab/schedules
|
||||
name: "[concat('shutdown-computevm-', variables('resourcePrefix'))]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
status: Enabled
|
||||
taskType: ComputeVmShutdownTask
|
||||
dailyRecurrence:
|
||||
time: 1900
|
||||
timeZoneId: GMT Standard Time
|
||||
notificationSettings:
|
||||
status: Enabled
|
||||
timeInMinutes: 15
|
||||
emailRecipient: "[parameters('email')]"
|
||||
targetResourceId: "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
- apiVersion: 2015-06-15
|
||||
# supports up to Ubuntu 18.04 LTS and RHEL 7.7, will fight join_domain_install_desktop for apt lock without dependency
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/linux/n-series-driver-setup
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/hpccompute-gpu-linux
|
||||
condition: "[contains(parameters('vmSize'), 'Standard_N')]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/nvidia')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/join_domain_install_desktop')]"
|
||||
properties:
|
||||
publisher: Microsoft.HpcCompute
|
||||
type: NvidiaGpuDriverLinux
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings: {}
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
owner:
|
||||
type: string
|
||||
value: "[variables('owner')]"
|
||||
email:
|
||||
type: string
|
||||
value: "[parameters('email')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
ipAddress:
|
||||
type: string
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table, example MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity"
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the local user account"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"defaultValue": "not classified",
|
||||
"metadata": {
|
||||
"description": "Uon Project Code"
|
||||
}
|
||||
},
|
||||
"toggleShutdownSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "f",
|
||||
"metadata": {
|
||||
"description": "t / f toggle for ComputeVmShutdownTask"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFWindows",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region"
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"dataDiskType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"metadata": {
|
||||
"description": "storage account type for data disk"
|
||||
}
|
||||
},
|
||||
"dataDiskSizeGB": {
|
||||
"type": "string",
|
||||
"defaultValue": "128",
|
||||
"metadata": {
|
||||
"description": "data disk size in GB"
|
||||
}
|
||||
},
|
||||
"domainToJoin": {
|
||||
"type": "string",
|
||||
"defaultValue": "ad.nottingham.ac.uk",
|
||||
"metadata": {
|
||||
"description": "The FQDN of the AD domain"
|
||||
}
|
||||
},
|
||||
"domainUsername": {
|
||||
"type": "string",
|
||||
"defaultValue": "service_CloudForms",
|
||||
"metadata": {
|
||||
"description": "Username of the account on the domain"
|
||||
}
|
||||
},
|
||||
"domainPassword": {
|
||||
"type": "securestring",
|
||||
"defaultValue": "As109pHY4Wi9o7naZnhr#!",
|
||||
"metadata": {
|
||||
"description": "Password of the account on the domain"
|
||||
}
|
||||
},
|
||||
"ouPath": {
|
||||
"type": "string",
|
||||
"defaultValue": "OU=AzureCloudForms_POC,OU=Testing,DC=ad,DC=nottingham,DC=ac,DC=uk",
|
||||
"metadata": {
|
||||
"description": "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"
|
||||
}
|
||||
},
|
||||
"domainJoinOptions": {
|
||||
"type": "int",
|
||||
"defaultValue": 3,
|
||||
"metadata": {
|
||||
"description": "Set of bit flags that define the join options. Default value of 3 is a combination of NETSETUP_JOIN_DOMAIN (0x00000001) & NETSETUP_ACCT_CREATE (0x00000002) i.e. will join the domain and create the account on the domain. For more information see https://msdn.microsoft.com/en-us/library/aa392154(v=vs.85).aspx"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"rdpgroupsCmd": "[concat('Add-LocalGroupMember -Group ''Remote Desktop Users'' -Member', ' ', variables('owner'))]",
|
||||
"localadminCmd": "[concat('Add-LocalGroupMember -Group Administrators -Member', ' ', variables('owner'))]",
|
||||
"disablePopupCmd": "[concat('\"Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask\"')]",
|
||||
"datadiskCmd": "[concat('\"Get-Disk | Where partitionstyle -eq ''raw'' | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel ''data'' -Confirm:$false\"')]",
|
||||
"installfirefoxCmd": "\"[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'));choco install -y firefox googlechrome\"",
|
||||
"powershellCmd": "[concat('powershell.exe -ExecutionPolicy Unrestricted', ' ', variables('rdpgroupsCmd'), ';', variables('localadminCmd'), ';', variables('disablePopupCmd'), ';', variables('datadiskCmd'), ';', variables('installfirefoxCmd'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"OS": "windows",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]",
|
||||
"windowsConfiguration": {
|
||||
"timeZone": "GMT Standard Time"
|
||||
}
|
||||
},
|
||||
"storageProfile": {
|
||||
"dataDisks": [
|
||||
{
|
||||
"diskSizeGB": "[parameters('dataDiskSizeGB')]",
|
||||
"lun": 0,
|
||||
"createOption": "Empty",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('dataDiskType')]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'joindomain')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Compute",
|
||||
"type": "JsonADDomainExtension",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"Name": "[parameters('domainToJoin')]",
|
||||
"OUPath": "[parameters('ouPath')]",
|
||||
"User": "[concat(parameters('domainUsername'), '@', parameters('domainToJoin'))]",
|
||||
"Restart": true,
|
||||
"Options": "[parameters('domainJoinOptions')]"
|
||||
},
|
||||
"protectedSettings": {
|
||||
"Password": "[parameters('domainPassword')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-06-01",
|
||||
"condition": "[not(or(equals(variables('owner'), 'unused'), equals(variables('owner'), 'donotreply')))]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/rdpgroups')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/joindomain')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Compute",
|
||||
"type": "CustomScriptExtension",
|
||||
"typeHandlerVersion": 1.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": "",
|
||||
"protectedSettings": {
|
||||
"commandToexecute": "[variables('powershellCmd')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-09-15",
|
||||
"condition": "[equals(parameters('toggleShutdownSchedule'), 't')]",
|
||||
"type": "Microsoft.DevTestLab/schedules",
|
||||
"name": "[concat('shutdown-computevm-', variables('resourcePrefix'))]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"status": "Enabled",
|
||||
"taskType": "ComputeVmShutdownTask",
|
||||
"dailyRecurrence": {
|
||||
"time": 1900
|
||||
},
|
||||
"timeZoneId": "GMT Standard Time",
|
||||
"notificationSettings": {
|
||||
"status": "Enabled",
|
||||
"timeInMinutes": 15,
|
||||
"emailRecipient": "[parameters('email')]"
|
||||
},
|
||||
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"condition": "[contains(parameters('vmSize'), 'Standard_N')]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/nvidia')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.HpcCompute",
|
||||
"type": "NvidiaGpuDriverWindows",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table, example MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the local user account
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
projectCode:
|
||||
type: string
|
||||
defaultValue: not classified
|
||||
metadata:
|
||||
description: Uon Project Code
|
||||
toggleShutdownSchedule:
|
||||
type: string
|
||||
defaultValue: f
|
||||
metadata:
|
||||
description: t / f toggle for ComputeVmShutdownTask
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFWindows
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
#defaultValue: unused
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: "[utcNow()]"
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
dataDiskType:
|
||||
type: string
|
||||
defaultValue: Standard_LRS
|
||||
metadata:
|
||||
description: storage account type for data disk
|
||||
dataDiskSizeGB:
|
||||
type: string
|
||||
defaultValue: "128"
|
||||
metadata:
|
||||
description: data disk size in GB
|
||||
domainToJoin:
|
||||
type: string
|
||||
defaultValue: ad.nottingham.ac.uk
|
||||
metadata:
|
||||
description: The FQDN of the AD domain
|
||||
domainUsername:
|
||||
type: string
|
||||
defaultValue: service_CloudForms
|
||||
metadata:
|
||||
description: Username of the account on the domain
|
||||
domainPassword:
|
||||
type: securestring
|
||||
defaultValue: As109pHY4Wi9o7naZnhr#!
|
||||
metadata:
|
||||
description: Password of the account on the domain
|
||||
ouPath:
|
||||
type: string
|
||||
defaultValue: OU=AzureCloudForms_POC,OU=Testing,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
metadata:
|
||||
description: "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"
|
||||
domainJoinOptions:
|
||||
type: int
|
||||
defaultValue: 3
|
||||
metadata:
|
||||
description: Set of bit flags that define the join options. Default value of 3 is a combination of NETSETUP_JOIN_DOMAIN (0x00000001) & NETSETUP_ACCT_CREATE (0x00000002) i.e. will join the domain and create the account on the domain. For more information see https://msdn.microsoft.com/en-us/library/aa392154(v=vs.85).aspx
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
rdpgroupsCmd: "[concat('Add-LocalGroupMember -Group ''Remote Desktop Users'' -Member', ' ', variables('owner'))]"
|
||||
localadminCmd: "[concat('Add-LocalGroupMember -Group Administrators -Member', ' ', variables('owner'))]"
|
||||
disablePopupCmd: "[concat('\"Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask\"')]"
|
||||
datadiskCmd: "[concat('\"Get-Disk | Where partitionstyle -eq ''raw'' | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel ''data'' -Confirm:$false\"')]"
|
||||
#setlocaleCmd: "[concat('Set-WinSystemLocale en-GB;Set-WinUserLanguageList -LanguageList en-GB -Force;Set-Culture -CultureInfo en-GB;Set-WinHomeLocation -GeoId 242;Set-TimeZone -Name \"GMT Standard Time\"')]" # setting the timezone in the osProfile sets these
|
||||
installfirefoxCmd: "\"[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'));choco install -y firefox googlechrome\""
|
||||
powershellCmd: "[concat('powershell.exe -ExecutionPolicy Unrestricted', ' ', variables('rdpgroupsCmd'), ';', variables('localadminCmd'), ';', variables('disablePopupCmd'), ';', variables('datadiskCmd'), ';', variables('installfirefoxCmd'))]"
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
OS: windows
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
windowsConfiguration:
|
||||
timeZone: GMT Standard Time
|
||||
storageProfile:
|
||||
dataDisks:
|
||||
- diskSizeGB: "[parameters('dataDiskSizeGB')]"
|
||||
lun: 0
|
||||
createOption: Empty
|
||||
managedDisk:
|
||||
storageAccountType: "[parameters('dataDiskType')]"
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2015-06-15
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'joindomain')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
publisher: Microsoft.Compute
|
||||
type: JsonADDomainExtension
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
Name: "[parameters('domainToJoin')]"
|
||||
OUPath: "[parameters('ouPath')]"
|
||||
User: "[concat(parameters('domainUsername'), '@', parameters('domainToJoin'))]"
|
||||
Restart: true
|
||||
Options: "[parameters('domainJoinOptions')]"
|
||||
protectedSettings:
|
||||
Password: "[parameters('domainPassword')]"
|
||||
- apiVersion: 2018-06-01
|
||||
# conditionally run if owner is a valid domain user and not a placeholder from this template or cloudforms admin user without valid uon email/account
|
||||
# condition: "[or(not(equals(variables('owner'), 'unused')), not(equals(variables('owner'), 'donotreply')))]" # wont match second condition
|
||||
condition: "[not(or(equals(variables('owner'), 'unused'), equals(variables('owner'), 'donotreply')))]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/rdpgroups')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/joindomain')]"
|
||||
properties:
|
||||
publisher: Microsoft.Compute
|
||||
type: CustomScriptExtension
|
||||
typeHandlerVersion: 1.10
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
protectedSettings:
|
||||
commandToexecute: "[variables('powershellCmd')]"
|
||||
# example for adding host entry when using default Azure dns
|
||||
# commandToExecute: powershell.exe -ExecutionPolicy Unrestricted Add-Content -Path "$env:windir\System32\drivers\etc\hosts" -Value "`r`n10.102.1.6`tad.nottingham.ac.uk" -Force
|
||||
- apiVersion: 2018-09-15
|
||||
condition: "[equals(parameters('toggleShutdownSchedule'), 't')]"
|
||||
type: Microsoft.DevTestLab/schedules
|
||||
name: "[concat('shutdown-computevm-', variables('resourcePrefix'))]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
status: Enabled
|
||||
taskType: ComputeVmShutdownTask
|
||||
dailyRecurrence:
|
||||
time: 1900
|
||||
timeZoneId: GMT Standard Time
|
||||
notificationSettings:
|
||||
status: Enabled
|
||||
timeInMinutes: 15
|
||||
emailRecipient: "[parameters('email')]"
|
||||
targetResourceId: "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
- apiVersion: 2015-06-15
|
||||
# seems to install driver on MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest if there are issues official support is for 2012 / 2016
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/windows/n-series-driver-setup
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/hpccompute-gpu-windows
|
||||
condition: "[contains(parameters('vmSize'), 'Standard_N')]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/nvidia')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
publisher: Microsoft.HpcCompute
|
||||
type: NvidiaGpuDriverWindows
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings: {}
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: "[deployment().name]"
|
||||
ipAddress:
|
||||
type: string
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "Name of the network security group"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "SSH",
|
||||
"properties": {
|
||||
"description": "Allow SSH traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 22,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerInBound",
|
||||
"properties": {
|
||||
"description": "allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "AzureLoadBalancer",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerOutbound",
|
||||
"properties": {
|
||||
"description": "allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "AzureLoadBalancer",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.1
|
||||
parameters:
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: Name of the network security group
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[parameters('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: SSH
|
||||
properties:
|
||||
description: Allow SSH traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 22
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerInBound
|
||||
properties:
|
||||
description: allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'AzureLoadBalancer'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Inbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetInBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Inbound
|
||||
- name: AllowAzureLoadBalancerOutbound
|
||||
properties:
|
||||
description: allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'AzureLoadBalancer'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Outbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetOutBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Outbound
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFWindows",
|
||||
"metadata": {
|
||||
"description": "Name of the network security group"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": "true"
|
||||
},
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "RDP",
|
||||
"properties": {
|
||||
"description": "Allow RDP traffic from anywhere",
|
||||
"protocol": "Tcp",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": 3389,
|
||||
"sourceAddressPrefix": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"access": "Allow",
|
||||
"priority": 100,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerInBound",
|
||||
"properties": {
|
||||
"description": "allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "AzureLoadBalancer",
|
||||
"destinationAddressPrefix": "VirtualNetwork",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Inbound"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "AllowAzureLoadBalancerOutbound",
|
||||
"properties": {
|
||||
"description": "allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services",
|
||||
"protocol": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationPortRange": "*",
|
||||
"sourceAddressPrefix": "VirtualNetwork",
|
||||
"destinationAddressPrefix": "AzureLoadBalancer",
|
||||
"access": "Allow",
|
||||
"priority": 3995,
|
||||
"direction": "Outbound"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.1
|
||||
parameters:
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFWindows
|
||||
metadata:
|
||||
description: Name of the network security group
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkSecurityGroups
|
||||
name: "[parameters('networkSecurityGroupName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: "true"
|
||||
properties:
|
||||
securityRules:
|
||||
- name: RDP
|
||||
properties:
|
||||
description: Allow RDP traffic from anywhere
|
||||
protocol: Tcp
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: 3389
|
||||
sourceAddressPrefix: '*'
|
||||
destinationAddressPrefix: '*'
|
||||
access: Allow
|
||||
priority: 100
|
||||
direction: Inbound
|
||||
- name: AllowAzureLoadBalancerInBound
|
||||
properties:
|
||||
description: allow essential Azure services from 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'AzureLoadBalancer'
|
||||
destinationAddressPrefix: 'VirtualNetwork'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Inbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetInBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Inbound
|
||||
- name: AllowAzureLoadBalancerOutbound
|
||||
properties:
|
||||
description: allow instances to essential Azure services 168.63.129.16, AzureLoadBalancer tag includes these services
|
||||
protocol: '*'
|
||||
sourcePortRange: '*'
|
||||
destinationPortRange: '*'
|
||||
sourceAddressPrefix: 'VirtualNetwork'
|
||||
destinationAddressPrefix: 'AzureLoadBalancer'
|
||||
access: Allow
|
||||
priority: 3995
|
||||
direction: Outbound
|
||||
# entry removed, we now use uon prod midtier vnet with routing on the same subnet range, this causes access issues
|
||||
# - name: DenyVnetOutBound
|
||||
# properties:
|
||||
# description: isolate instances on the same range from each another
|
||||
# protocol: '*'
|
||||
# sourcePortRange: '*'
|
||||
# destinationPortRange: '*'
|
||||
# sourceAddressPrefix: 'VirtualNetwork'
|
||||
# destinationAddressPrefix: 'VirtualNetwork'
|
||||
# access: Deny
|
||||
# priority: 3996
|
||||
# direction: Outbound
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
#!/bin/bash
|
||||
exec 1> /root/$0.log 2>&1
|
||||
set -x
|
||||
|
||||
# ubuntu much prefers cloud-init
|
||||
# should be rewritten with a common function to wait for apt db and metadata availability with timeouts
|
||||
|
||||
# Let the system init correctly
|
||||
sleep 20
|
||||
|
||||
# stop ssh to ensure no user login during domain join + update, disable this for debug
|
||||
systemctl stop sshd
|
||||
|
||||
# set tz
|
||||
timedatectl set-timezone Europe/London
|
||||
|
||||
# join domain
|
||||
# wait loop until apt database is not locked to avoid clash with Azure policy installs, add a little delay to be able to contact apt repos (probably not Azure ones?)
|
||||
aptupdate=1
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
until [ $aptupdate -eq 0 ];
|
||||
do
|
||||
apt-get -y update
|
||||
apt-get -y install jq
|
||||
which jq
|
||||
aptupdate=$?
|
||||
sleep 5
|
||||
done
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y install krb5-user samba sssd sssd-tools libnss-sss libpam-sss realmd adcli expect
|
||||
|
||||
cat > /etc/krb5.conf <<EOF
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = AD.NOTTINGHAM.AC.UK
|
||||
dns_lookup_realm = true
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
rdns = false
|
||||
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
|
||||
#default_ccache_name = KEYRING:persistent:%{uid}
|
||||
|
||||
[realms]
|
||||
AD.NOTTINGHAM.AC.UK = {
|
||||
kdc = AD.NOTTINGHAM.AC.UK
|
||||
admin_server = AD.NOTTINGHAM.AC.UK
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
.ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
EOF
|
||||
|
||||
# password cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# if the password changes this script must be reprocessed and the ARM template updated in the CloudForms orchestration template
|
||||
echo 'As109pHY4Wi9o7naZnhr#!' | kinit -V service_CloudForms@AD.NOTTINGHAM.AC.UK
|
||||
cat > /etc/realmd.conf <<EOF
|
||||
[active-directory]
|
||||
default-client = sssd
|
||||
|
||||
[service]
|
||||
automatic-install = yes
|
||||
|
||||
[ad.nottingham.ac.uk]
|
||||
manage-system = no
|
||||
automatic-id-mapping = yes
|
||||
computer-name = $(hostname -s)
|
||||
computer-ou = ou=AzureCloudForms_POC,ou=Testing,dc=ad,dc=nottingham,dc=ac,dc=uk
|
||||
EOF
|
||||
|
||||
systemctl restart realmd
|
||||
realm join AD.NOTTINGHAM.AC.UK --install=/
|
||||
|
||||
# owner cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# query metadata tag to find owner, this will be used in the sssd.conf whitelist and sudoers
|
||||
owner=$(curl -sH Metadata:true "http://169.254.169.254/metadata/instance?api-version=2019-06-01" | jq .compute.tags | sed 's/\"//g' | awk -F ";" '{for(i=1;i<=NF;i++)if($i~/Owner:/) split($i,result,":");print result[2] }')
|
||||
if [ -z "$owner" ]; then
|
||||
owner=missingtag
|
||||
fi
|
||||
|
||||
cat > /etc/sssd/sssd.conf <<EOF
|
||||
[sssd]
|
||||
domains = ad.nottingham.ac.uk
|
||||
config_file_version = 2
|
||||
services = nss, pam
|
||||
|
||||
[domain/ad.nottingham.ac.uk]
|
||||
ad_domain = ad.nottingham.ac.uk
|
||||
krb5_realm = AD.NOTTINGHAM.AC.UK
|
||||
realmd_tags = joined-with-adcli
|
||||
cache_credentials = True
|
||||
id_provider = ad
|
||||
krb5_store_password_if_offline = True
|
||||
default_shell = /bin/bash
|
||||
ldap_sasl_authid = $(hostname -s)$
|
||||
ldap_id_mapping = True
|
||||
fallback_homedir = /home/%u@%d
|
||||
auth_provider = ad
|
||||
# uon specific with computer account object with no dns
|
||||
use_fully_qualified_names = False
|
||||
enumerate = False
|
||||
# uon large directory performance options
|
||||
ignore_group_members = True
|
||||
entry_cache_timeout = 600
|
||||
# uon search base tuning - target OU for large directory
|
||||
ldap_user_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
#ldap_group_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
ldap_use_tokengroups = False
|
||||
# restrict access to owner
|
||||
access_provider = simple
|
||||
simple_allow_users = $owner
|
||||
EOF
|
||||
|
||||
# stop sssd until update finished
|
||||
systemctl stop sssd
|
||||
systemctl enable sssd
|
||||
|
||||
# setup sudoers
|
||||
cat > /etc/sudoers.d/nottingham <<EOF
|
||||
$owner ALL=(ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
#install desktop, mate desktop meta package requires user interaction to select window manager, expect doesnt play well in whatever shell this script is run from - this minimal install is quicker @10mins
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y --no-install-recommends install x2goserver firefox caja compiz-mate engrampa eom folder-color-caja gnome-accessibility-themes gnome-colors-common gnome-icon-theme gnome-orca grub2-themes-ubuntu-mate indicator-messages indicator-power indicator-session indicator-sound lightdm-gtk-greeter mate-core mate-accessibility-profiles mate-applet-appmenu mate-applet-brisk-menu mate-calc mate-desktop mate-dock-applet mate-hud mate-icon-theme mate-menu mate-menus mate-netbook mate-optimus mate-screensaver mate-screensaver-common mate-system-monitor mate-tweak mate-user-guide mate-utils mate-window-applets-common mate-window-buttons-applet mate-window-menu-applet mate-window-title-applet plank plymouth-theme-ubuntu-mate-logo plymouth-theme-ubuntu-mate-text sessioninstaller sound-theme-freedesktop tilda ubuntu-mate-artwork ubuntu-mate-core ubuntu-mate-default-settings ubuntu-mate-guide ubuntu-mate-icon-themes ubuntu-mate-lightdm-theme ubuntu-mate-themes ubuntu-mate-wallpapers* ubuntu-standard
|
||||
|
||||
# enable home directory creation at logon - perform after desktop install to avoid manual prompts with desktop install
|
||||
sed -i 's/^.*pam_sss.so.*$/&\nsession required\tpam_mkhomedir.so skel=\/etc\/skel\/ umask=0077/' /etc/pam.d/common-session
|
||||
|
||||
# add secondary disk
|
||||
parted -s /dev/disk/azure/scsi1/lun0 -- mklabel gpt mkpart primary xfs 0% 100%
|
||||
sleep 2 # allow partition to register
|
||||
mkfs.xfs /dev/disk/azure/scsi1/lun0-part1
|
||||
sleep 2 # allow filesystem creation to finish
|
||||
xfs_admin -L uondata /dev/disk/azure/scsi1/lun0-part1
|
||||
mkdir /uondata
|
||||
echo "LABEL=uondata /uondata xfs defaults 0 0" >> /etc/fstab
|
||||
mount -a
|
||||
chmod 777 /uondata
|
||||
|
||||
# install carbon black
|
||||
wget -O azcopy_v10.tar.gz https://aka.ms/downloadazcopy-v10-linux
|
||||
tar -xf azcopy_v10.tar.gz --strip-components=1
|
||||
mv azcopy /usr/local/bin/
|
||||
chmod +x /usr/local/bin/azcopy
|
||||
rm -Rf azcopy*
|
||||
mkdir /root/uon_packages
|
||||
identretry=5 # loop with timeout until managed identity is available
|
||||
msident=1
|
||||
until [ $msident -eq 0 ] || [ $identretry -eq 0 ];
|
||||
do
|
||||
identretry=$((identretry-1))
|
||||
azcopy login --identity
|
||||
msident=$?
|
||||
sleep 5
|
||||
done
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/CarbonBlackClientSetup-linux-v7.0.0.14291.tgz' '/root/uon_packages'
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/CarbonBlackClientSetup-linux-v7.0.0.14291.sh' '/root/uon_packages'
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/sensorsettings.ini' '/root/uon_packages'
|
||||
chmod +x /root/uon_packages/CarbonBlackClientSetup-linux-v7.0.0.14291.sh
|
||||
cd /root/uon_packages
|
||||
#while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done # installer script builds apt packages that can get blocked by the nvidia extension
|
||||
./CarbonBlackClientSetup-linux-v7.0.0.14291.sh
|
||||
cd /root
|
||||
rm -Rf /root/uon_packages
|
||||
|
||||
# restart services for domain user login
|
||||
systemctl restart sssd
|
||||
systemctl restart sshd
|
||||
|
||||
#Azure extensions dont like a reboot, dont update without a reboot to ensure no system artifacts
|
||||
#apt-get -y upgrade
|
||||
#reboot
|
||||
|
||||
#exit gracefully for waagent
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "Canonical:UbuntuServer:18.04-LTS:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table, example Canonical:UbuntuServer:18.04-LTS:latest"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity"
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the local user account"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"defaultValue": "not classified",
|
||||
"metadata": {
|
||||
"description": "Uon Project Code"
|
||||
}
|
||||
},
|
||||
"toggleShutdownSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "f",
|
||||
"metadata": {
|
||||
"description": "t / f toggle for ComputeVmShutdownTask"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFLinux",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region."
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"dataDiskType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"metadata": {
|
||||
"description": "storage account type for data disk"
|
||||
}
|
||||
},
|
||||
"dataDiskSizeGB": {
|
||||
"type": "string",
|
||||
"defaultValue": "128",
|
||||
"metadata": {
|
||||
"description": "data disk size in GB"
|
||||
}
|
||||
},
|
||||
"domainToJoin": {
|
||||
"type": "string",
|
||||
"defaultValue": "ad.nottingham.ac.uk",
|
||||
"metadata": {
|
||||
"description": "The FQDN of the AD domain"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"identityName": "extensionartefact"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"OS": "linux",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"identity": {
|
||||
"type": "UserAssigned",
|
||||
"userAssignedIdentities": {
|
||||
"[resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName'))]": {}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"dataDisks": [
|
||||
{
|
||||
"diskSizeGB": "[parameters('dataDiskSizeGB')]",
|
||||
"lun": 0,
|
||||
"createOption": "Empty",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('dataDiskType')]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'customscript')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Azure.Extensions",
|
||||
"type": "CustomScript",
|
||||
"typeHandlerVersion": 2.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"skipDos2Unix": false
|
||||
},
|
||||
"protectedSettings": {
|
||||
"fileUris": [
|
||||
"https://extensionartefact.blob.core.windows.net/extensionartefact/Azure_UbuntuServer_customscript.sh"
|
||||
],
|
||||
"commandToExecute": "sudo sh Azure_UbuntuServer_customscript.sh",
|
||||
"managedIdentity": {
|
||||
"clientid": "[reference(resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName')), '2018-11-30', 'full').properties.clientId]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-09-15",
|
||||
"condition": "[equals(parameters('toggleShutdownSchedule'), 't')]",
|
||||
"type": "Microsoft.DevTestLab/schedules",
|
||||
"name": "[concat('shutdown-computevm-', variables('resourcePrefix'))]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"status": "Enabled",
|
||||
"taskType": "ComputeVmShutdownTask",
|
||||
"dailyRecurrence": {
|
||||
"time": 1900
|
||||
},
|
||||
"timeZoneId": "GMT Standard Time",
|
||||
"notificationSettings": {
|
||||
"status": "Enabled",
|
||||
"timeInMinutes": 15,
|
||||
"emailRecipient": "[parameters('email')]"
|
||||
},
|
||||
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"condition": "[contains(parameters('vmSize'), 'Standard_N')]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/nvidia')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/customscript')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.HpcCompute",
|
||||
"type": "NvidiaGpuDriverLinux",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"value": "[variables('owner')]"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"value": "[parameters('email')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: Canonical:UbuntuServer:18.04-LTS:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table, example Canonical:UbuntuServer:18.04-LTS:latest
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the local user account
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
projectCode:
|
||||
type: string
|
||||
defaultValue: not classified
|
||||
metadata:
|
||||
description: Uon Project Code
|
||||
toggleShutdownSchedule:
|
||||
type: string
|
||||
defaultValue: f
|
||||
metadata:
|
||||
description: t / f toggle for ComputeVmShutdownTask
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFLinux
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
#defaultValue: unused
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region.
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: '[utcNow()]'
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
dataDiskType:
|
||||
type: string
|
||||
defaultValue: Standard_LRS
|
||||
metadata:
|
||||
description: storage account type for data disk
|
||||
dataDiskSizeGB:
|
||||
type: string
|
||||
defaultValue: "128"
|
||||
metadata:
|
||||
description: data disk size in GB
|
||||
domainToJoin:
|
||||
type: string
|
||||
defaultValue: ad.nottingham.ac.uk
|
||||
metadata:
|
||||
description: The FQDN of the AD domain
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
identityName: extensionartefact
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
OS: linux
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
identity:
|
||||
type: UserAssigned
|
||||
userAssignedIdentities:
|
||||
#"[resourceID('Microsoft.ManagedIdentity/userAssignedIdentities/',variables('identityName'))]": {}
|
||||
"[resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName'))]": {}
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
storageProfile:
|
||||
dataDisks:
|
||||
- diskSizeGB: "[parameters('dataDiskSizeGB')]"
|
||||
lun: 0
|
||||
createOption: Empty
|
||||
managedDisk:
|
||||
storageAccountType: "[parameters('dataDiskType')]"
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'customscript')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
publisher: Microsoft.Azure.Extensions
|
||||
type: CustomScript
|
||||
typeHandlerVersion: 2.1
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
skipDos2Unix: false
|
||||
protectedSettings:
|
||||
fileUris:
|
||||
- https://extensionartefact.blob.core.windows.net/extensionartefact/Azure_UbuntuServer_customscript.sh
|
||||
commandToExecute: "sudo sh Azure_UbuntuServer_customscript.sh"
|
||||
managedIdentity:
|
||||
clientid: "[reference(resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName')), '2018-11-30', 'full').properties.clientId]"
|
||||
- apiVersion: 2018-09-15
|
||||
condition: "[equals(parameters('toggleShutdownSchedule'), 't')]"
|
||||
type: Microsoft.DevTestLab/schedules
|
||||
name: "[concat('shutdown-computevm-', variables('resourcePrefix'))]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
status: Enabled
|
||||
taskType: ComputeVmShutdownTask
|
||||
dailyRecurrence:
|
||||
time: 1900
|
||||
timeZoneId: GMT Standard Time
|
||||
notificationSettings:
|
||||
status: Enabled
|
||||
timeInMinutes: 15
|
||||
emailRecipient: "[parameters('email')]"
|
||||
targetResourceId: "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
- apiVersion: 2015-06-15
|
||||
# supports up to Ubuntu 18.04 LTS and RHEL 7.7, will fight customscript for apt lock without dependency
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/linux/n-series-driver-setup
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/hpccompute-gpu-linux
|
||||
condition: "[contains(parameters('vmSize'), 'Standard_N')]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/nvidia')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/customscript')]"
|
||||
properties:
|
||||
publisher: Microsoft.HpcCompute
|
||||
type: NvidiaGpuDriverLinux
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings: {}
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
owner:
|
||||
type: string
|
||||
value: "[variables('owner')]"
|
||||
email:
|
||||
type: string
|
||||
value: "[parameters('email')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: '[deployment().name]'
|
||||
ipAddress:
|
||||
type: string
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,410 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"imageUrn": {
|
||||
"type": "string",
|
||||
"defaultValue": "MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest",
|
||||
"metadata": {
|
||||
"description": "az vm image list --output table / az vm image list -p RedHat --all --output table, example MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest"
|
||||
}
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity"
|
||||
}
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Password for the local user account"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"defaultValue": "unused",
|
||||
"metadata": {
|
||||
"description": "UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled"
|
||||
}
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"defaultValue": "not classified",
|
||||
"metadata": {
|
||||
"description": "Uon Project Code"
|
||||
}
|
||||
},
|
||||
"toggleShutdownSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "f",
|
||||
"metadata": {
|
||||
"description": "t / f toggle for ComputeVmShutdownTask"
|
||||
}
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_B2s",
|
||||
"metadata": {
|
||||
"description": "Virtual machine size."
|
||||
}
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string",
|
||||
"defaultValue": "CFWindows",
|
||||
"metadata": {
|
||||
"description": "NSG name for CF Linux instances"
|
||||
}
|
||||
},
|
||||
"networkResourceGroup": {
|
||||
"type": "string",
|
||||
"defaultValue": "rg-vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'"
|
||||
}
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string",
|
||||
"defaultValue": "vn-rem-we-1",
|
||||
"metadata": {
|
||||
"description": "Virtual network for CF instances"
|
||||
}
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string",
|
||||
"defaultValue": "sn-vn-rem-we-1-midtier-1",
|
||||
"metadata": {
|
||||
"description": "Name of a subnet in the virtual network"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "West Europe",
|
||||
"metadata": {
|
||||
"description": "Location for all resources, defaults to resource group region"
|
||||
}
|
||||
},
|
||||
"utcValue": {
|
||||
"type": "string",
|
||||
"defaultValue": "[utcNow()]"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"defaultValue": "[uniqueString(resourceGroup().id, parameters('utcValue'))]",
|
||||
"metadata": {
|
||||
"description": "passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix"
|
||||
}
|
||||
},
|
||||
"dataDiskType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"metadata": {
|
||||
"description": "storage account type for data disk"
|
||||
}
|
||||
},
|
||||
"dataDiskSizeGB": {
|
||||
"type": "string",
|
||||
"defaultValue": "128",
|
||||
"metadata": {
|
||||
"description": "data disk size in GB"
|
||||
}
|
||||
},
|
||||
"domainToJoin": {
|
||||
"type": "string",
|
||||
"defaultValue": "ad.nottingham.ac.uk",
|
||||
"metadata": {
|
||||
"description": "The FQDN of the AD domain"
|
||||
}
|
||||
},
|
||||
"domainUsername": {
|
||||
"type": "string",
|
||||
"defaultValue": "service_CloudForms",
|
||||
"metadata": {
|
||||
"description": "Username of the account on the domain"
|
||||
}
|
||||
},
|
||||
"domainPassword": {
|
||||
"type": "securestring",
|
||||
"defaultValue": "As109pHY4Wi9o7naZnhr#!",
|
||||
"metadata": {
|
||||
"description": "Password of the account on the domain"
|
||||
}
|
||||
},
|
||||
"ouPath": {
|
||||
"type": "string",
|
||||
"defaultValue": "OU=AzureCloudForms_POC,OU=Testing,DC=ad,DC=nottingham,DC=ac,DC=uk",
|
||||
"metadata": {
|
||||
"description": "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"
|
||||
}
|
||||
},
|
||||
"domainJoinOptions": {
|
||||
"type": "int",
|
||||
"defaultValue": 3,
|
||||
"metadata": {
|
||||
"description": "Set of bit flags that define the join options. Default value of 3 is a combination of NETSETUP_JOIN_DOMAIN (0x00000001) & NETSETUP_ACCT_CREATE (0x00000002) i.e. will join the domain and create the account on the domain. For more information see https://msdn.microsoft.com/en-us/library/aa392154(v=vs.85).aspx"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"emailAttributes": "[split(parameters('email'),'@')]",
|
||||
"owner": "[variables('emailAttributes')[0]]",
|
||||
"urnAttributes": "[split(parameters('imageUrn'),':')]",
|
||||
"imagePublisher": "[variables('urnAttributes')[0]]",
|
||||
"imageOffer": "[variables('urnAttributes')[1]]",
|
||||
"imageSku": "[variables('urnAttributes')[2]]",
|
||||
"imageVersion": "[variables('urnAttributes')[3]]",
|
||||
"resourcePrefix": "[parameters('prefix')]",
|
||||
"vmName": "[variables('resourcePrefix')]",
|
||||
"nicName": "[concat(variables('resourcePrefix'), '-nic')]",
|
||||
"osDiskType": "StandardSSD_LRS",
|
||||
"networkSecurityGroupId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"identityName": "extensionartefact"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-11-01",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"name": "[variables('nicName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"OS": "windows",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"subnet": {
|
||||
"id": "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('networkSecurityGroupId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2019-12-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[variables('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"identity": {
|
||||
"type": "UserAssigned",
|
||||
"userAssignedIdentities": {
|
||||
"[resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName'))]": {}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"CFSKU": "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('vmName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"adminPassword": "[parameters('adminPassword')]",
|
||||
"windowsConfiguration": {
|
||||
"timeZone": "GMT Standard Time"
|
||||
}
|
||||
},
|
||||
"storageProfile": {
|
||||
"dataDisks": [
|
||||
{
|
||||
"diskSizeGB": "[parameters('dataDiskSizeGB')]",
|
||||
"lun": 0,
|
||||
"createOption": "Empty",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('dataDiskType')]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"osDisk": {
|
||||
"createOption": "FromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[variables('osDiskType')]"
|
||||
}
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "[variables('imagePublisher')]",
|
||||
"offer": "[variables('imageOffer')]",
|
||||
"sku": "[variables('imageSku')]",
|
||||
"version": "[variables('imageVersion')]"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]",
|
||||
"properties": {
|
||||
"primary": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'joindomain')]",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Compute",
|
||||
"type": "JsonADDomainExtension",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"Name": "[parameters('domainToJoin')]",
|
||||
"OUPath": "[parameters('ouPath')]",
|
||||
"User": "[concat(parameters('domainUsername'), '@', parameters('domainToJoin'))]",
|
||||
"Restart": true,
|
||||
"Options": "[parameters('domainJoinOptions')]"
|
||||
},
|
||||
"protectedSettings": {
|
||||
"Password": "[parameters('domainPassword')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-06-01",
|
||||
"condition": "[not(or(equals(variables('owner'), 'unused'), equals(variables('owner'), 'donotreply')))]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/', 'customscript')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"ADDomain": "[parameters('domainToJoin')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/joindomain')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.Compute",
|
||||
"type": "CustomScriptExtension",
|
||||
"typeHandlerVersion": 1.1,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {
|
||||
"fileUris": [
|
||||
"https://extensionartefact.blob.core.windows.net/extensionartefact/Azure_WindowsServer_customscript.ps1"
|
||||
]
|
||||
},
|
||||
"protectedSettings": {
|
||||
"commandToExecute": "[concat('powershell.exe -ExecutionPolicy Unrestricted -File Azure_WindowsServer_customscript.ps1 -user', ' ', variables('owner'))]",
|
||||
"managedIdentity": {
|
||||
"clientid": "[reference(resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName')), '2018-11-30', 'full').properties.clientId]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2018-09-15",
|
||||
"condition": "[equals(parameters('toggleShutdownSchedule'), 't')]",
|
||||
"type": "Microsoft.DevTestLab/schedules",
|
||||
"name": "[concat('shutdown-computevm-', variables('resourcePrefix'))]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"status": "Enabled",
|
||||
"taskType": "ComputeVmShutdownTask",
|
||||
"dailyRecurrence": {
|
||||
"time": 1900
|
||||
},
|
||||
"timeZoneId": "GMT Standard Time",
|
||||
"notificationSettings": {
|
||||
"status": "Enabled",
|
||||
"timeInMinutes": 15,
|
||||
"emailRecipient": "[parameters('email')]"
|
||||
},
|
||||
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-06-15",
|
||||
"condition": "[contains(parameters('vmSize'), 'Standard_N')]",
|
||||
"type": "Microsoft.Compute/virtualMachines/extensions",
|
||||
"name": "[concat(variables('resourcePrefix'), '/nvidia')]",
|
||||
"location": "[parameters('location')]",
|
||||
"tags": {
|
||||
"CFManaged": true,
|
||||
"Owner": "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]",
|
||||
"OwnerMail": "[parameters('email')]",
|
||||
"Cost Center": "[parameters('projectCode')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
|
||||
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/customscript')]"
|
||||
],
|
||||
"properties": {
|
||||
"publisher": "Microsoft.HpcCompute",
|
||||
"type": "NvidiaGpuDriverWindows",
|
||||
"typeHandlerVersion": 1.3,
|
||||
"autoUpgradeMinorVersion": true,
|
||||
"settings": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"utcOutput": {
|
||||
"type": "string",
|
||||
"value": "[parameters('utcValue')]"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
},
|
||||
"adminPassword": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminPassword')]"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string",
|
||||
"value": "[variables('vmName')]"
|
||||
},
|
||||
"deploymentName": {
|
||||
"type": "string",
|
||||
"value": "[deployment().name]"
|
||||
},
|
||||
"ipAddress": {
|
||||
"type": "string",
|
||||
"value": "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
$schema: https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
|
||||
contentVersion: 1.0.0.0
|
||||
parameters:
|
||||
imageUrn:
|
||||
type: string
|
||||
defaultValue: MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest
|
||||
metadata:
|
||||
description: az vm image list --output table / az vm image list -p RedHat --all --output table, example MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest
|
||||
adminUsername:
|
||||
type: string
|
||||
metadata:
|
||||
description: Local user account for the instance, intended to be used by UoN ops where the instance has no domain connectivity
|
||||
adminPassword:
|
||||
type: securestring
|
||||
metadata:
|
||||
description: Password for the local user account
|
||||
email:
|
||||
type: string
|
||||
defaultValue: unused
|
||||
metadata:
|
||||
description: UoN requester email supplied by cloudforms, where value is 'unused' or 'donotreply@nottingham.ac.uk' the extension rdpgroups is disabled
|
||||
projectCode:
|
||||
type: string
|
||||
defaultValue: not classified
|
||||
metadata:
|
||||
description: Uon Project Code
|
||||
toggleShutdownSchedule:
|
||||
type: string
|
||||
defaultValue: f
|
||||
metadata:
|
||||
description: t / f toggle for ComputeVmShutdownTask
|
||||
vmSize:
|
||||
type: string
|
||||
defaultValue: Standard_B2s
|
||||
metadata:
|
||||
description: Virtual machine size.
|
||||
networkSecurityGroupName:
|
||||
type: string
|
||||
defaultValue: CFWindows
|
||||
metadata:
|
||||
description: NSG name for CF Linux instances
|
||||
networkResourceGroup:
|
||||
type: string
|
||||
#defaultValue: unused
|
||||
defaultValue: rg-vn-rem-we-1
|
||||
metadata:
|
||||
description: Populate if the vnet+subnet are in a different resource group (same location) than the instance, otherwise set defaultValue as 'unused'
|
||||
virtualNetworkName:
|
||||
type: string
|
||||
defaultValue: vn-rem-we-1
|
||||
metadata:
|
||||
description: Virtual network for CF instances
|
||||
subnetName:
|
||||
type: string
|
||||
defaultValue: sn-vn-rem-we-1-midtier-1
|
||||
metadata:
|
||||
description: Name of a subnet in the virtual network
|
||||
location:
|
||||
type: string
|
||||
defaultValue: West Europe
|
||||
metadata:
|
||||
description: Location for all resources, defaults to resource group region
|
||||
utcValue:
|
||||
type: string
|
||||
defaultValue: "[utcNow()]"
|
||||
prefix:
|
||||
type: string
|
||||
defaultValue: "[uniqueString(resourceGroup().id, parameters('utcValue'))]"
|
||||
metadata:
|
||||
description: passed as param_prefix from cloudforms, the cloudforms stack_name (aka deployment template name) has the same value, default value is to get deterministic hash of resource group and time now for a unique prefix
|
||||
dataDiskType:
|
||||
type: string
|
||||
defaultValue: Standard_LRS
|
||||
metadata:
|
||||
description: storage account type for data disk
|
||||
dataDiskSizeGB:
|
||||
type: string
|
||||
defaultValue: "128"
|
||||
metadata:
|
||||
description: data disk size in GB
|
||||
domainToJoin:
|
||||
type: string
|
||||
defaultValue: ad.nottingham.ac.uk
|
||||
metadata:
|
||||
description: The FQDN of the AD domain
|
||||
domainUsername:
|
||||
type: string
|
||||
defaultValue: service_CloudForms
|
||||
metadata:
|
||||
description: Username of the account on the domain
|
||||
domainPassword:
|
||||
type: securestring
|
||||
defaultValue: As109pHY4Wi9o7naZnhr#!
|
||||
metadata:
|
||||
description: Password of the account on the domain
|
||||
ouPath:
|
||||
type: string
|
||||
defaultValue: OU=AzureCloudForms_POC,OU=Testing,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
metadata:
|
||||
description: "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"
|
||||
domainJoinOptions:
|
||||
type: int
|
||||
defaultValue: 3
|
||||
metadata:
|
||||
description: Set of bit flags that define the join options. Default value of 3 is a combination of NETSETUP_JOIN_DOMAIN (0x00000001) & NETSETUP_ACCT_CREATE (0x00000002) i.e. will join the domain and create the account on the domain. For more information see https://msdn.microsoft.com/en-us/library/aa392154(v=vs.85).aspx
|
||||
variables:
|
||||
emailAttributes: "[split(parameters('email'),'@')]"
|
||||
owner: "[variables('emailAttributes')[0]]"
|
||||
urnAttributes: "[split(parameters('imageUrn'),':')]"
|
||||
imagePublisher: "[variables('urnAttributes')[0]]"
|
||||
imageOffer: "[variables('urnAttributes')[1]]"
|
||||
imageSku: "[variables('urnAttributes')[2]]"
|
||||
imageVersion: "[variables('urnAttributes')[3]]"
|
||||
resourcePrefix: "[parameters('prefix')]"
|
||||
vmName: "[variables('resourcePrefix')]"
|
||||
nicName: "[concat(variables('resourcePrefix'), '-nic')]"
|
||||
osDiskType: StandardSSD_LRS
|
||||
networkSecurityGroupId: "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
|
||||
# subnetRef: "[resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in another rg
|
||||
# subnetRef: "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" # subnet in same rg
|
||||
identityName: extensionartefact
|
||||
#rdpgroupsCmd: "[concat('Add-LocalGroupMember -Group ''Remote Desktop Users'' -Member', ' ', variables('owner'))]"
|
||||
#localadminCmd: "[concat('Add-LocalGroupMember -Group Administrators -Member', ' ', variables('owner'))]"
|
||||
#disablePopupCmd: "[concat('\"Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask\"')]"
|
||||
#datadiskCmd: "[concat('\"Get-Disk | Where partitionstyle -eq ''raw'' | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel ''data'' -Confirm:$false\"')]"
|
||||
##setlocaleCmd: "[concat('Set-WinSystemLocale en-GB;Set-WinUserLanguageList -LanguageList en-GB -Force;Set-Culture -CultureInfo en-GB;Set-WinHomeLocation -GeoId 242;Set-TimeZone -Name \"GMT Standard Time\"')]" # setting the timezone in the osProfile sets these
|
||||
#installfirefoxCmd: "\"[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'));choco install -y firefox googlechrome\""
|
||||
#powershellCmd: "[concat('powershell.exe -ExecutionPolicy Unrestricted', ' ', variables('rdpgroupsCmd'), ';', variables('localadminCmd'), ';', variables('disablePopupCmd'), ';', variables('datadiskCmd'), ';', variables('installfirefoxCmd'))]"
|
||||
resources:
|
||||
- apiVersion: 2019-11-01
|
||||
type: Microsoft.Network/networkInterfaces
|
||||
name: "[variables('nicName')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
OS: windows
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- name: ipconfig1
|
||||
properties:
|
||||
privateIPAllocationMethod: Dynamic
|
||||
subnet:
|
||||
#id: "[variables('subnetRef')]" # now use conditional so we can use the subnet in the native RG's vnet or another RG's vnet
|
||||
id: "[if(equals(parameters('networkResourceGroup'), 'unused'), resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), resourceId(parameters('networkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')))]"
|
||||
networkSecurityGroup:
|
||||
id: "[variables('networkSecurityGroupId')]"
|
||||
- apiVersion: 2019-12-01
|
||||
type: Microsoft.Compute/virtualMachines
|
||||
name: "[variables('vmName')]"
|
||||
location: "[parameters('location')]"
|
||||
identity:
|
||||
type: UserAssigned
|
||||
userAssignedIdentities:
|
||||
#"[resourceID('Microsoft.ManagedIdentity/userAssignedIdentities/',variables('identityName'))]": {}
|
||||
"[resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName'))]": {}
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
CFSKU: "[concat(variables('imagePublisher'), ':', variables('imageOffer'), ':', variables('imageSku'), ':', variables('imageVersion'))]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "[parameters('vmSize')]"
|
||||
osProfile:
|
||||
computerName: "[variables('vmName')]"
|
||||
adminUsername: "[parameters('adminUsername')]"
|
||||
adminPassword: "[parameters('adminPassword')]"
|
||||
windowsConfiguration:
|
||||
timeZone: GMT Standard Time
|
||||
storageProfile:
|
||||
dataDisks:
|
||||
- diskSizeGB: "[parameters('dataDiskSizeGB')]"
|
||||
lun: 0
|
||||
createOption: Empty
|
||||
managedDisk:
|
||||
storageAccountType: "[parameters('dataDiskType')]"
|
||||
osDisk:
|
||||
createOption: FromImage
|
||||
managedDisk:
|
||||
storageAccountType: "[variables('osDiskType')]"
|
||||
imageReference:
|
||||
publisher: "[variables('imagePublisher')]"
|
||||
offer: "[variables('imageOffer')]"
|
||||
sku: "[variables('imageSku')]"
|
||||
version: "[variables('imageVersion')]"
|
||||
networkProfile:
|
||||
networkInterfaces:
|
||||
- id: "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
properties:
|
||||
primary: true
|
||||
- apiVersion: 2015-06-15
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'joindomain')]"
|
||||
location: "[parameters('location')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
properties:
|
||||
publisher: Microsoft.Compute
|
||||
type: JsonADDomainExtension
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
Name: "[parameters('domainToJoin')]"
|
||||
OUPath: "[parameters('ouPath')]"
|
||||
User: "[concat(parameters('domainUsername'), '@', parameters('domainToJoin'))]"
|
||||
Restart: true
|
||||
Options: "[parameters('domainJoinOptions')]"
|
||||
protectedSettings:
|
||||
Password: "[parameters('domainPassword')]"
|
||||
- apiVersion: 2018-06-01
|
||||
# conditionally run if owner is a valid domain user and not a placeholder from this template or cloudforms admin user without valid uon email/account
|
||||
# condition: "[or(not(equals(variables('owner'), 'unused')), not(equals(variables('owner'), 'donotreply')))]" # wont match second condition
|
||||
condition: "[not(or(equals(variables('owner'), 'unused'), equals(variables('owner'), 'donotreply')))]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
#name: "[concat(variables('resourcePrefix'), '/rdpgroups')]"
|
||||
name: "[concat(variables('resourcePrefix'), '/', 'customscript')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
ADDomain: "[parameters('domainToJoin')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/joindomain')]"
|
||||
properties:
|
||||
publisher: Microsoft.Compute
|
||||
type: CustomScriptExtension
|
||||
typeHandlerVersion: 1.10
|
||||
autoUpgradeMinorVersion: true
|
||||
settings:
|
||||
fileUris:
|
||||
- https://extensionartefact.blob.core.windows.net/extensionartefact/Azure_WindowsServer_customscript.ps1
|
||||
protectedSettings:
|
||||
#commandToExecute: "powershell.exe Azure_WindowsServer_customscript"
|
||||
commandToExecute: "[concat('powershell.exe -ExecutionPolicy Unrestricted -File Azure_WindowsServer_customscript.ps1 -user', ' ', variables('owner'))]"
|
||||
managedIdentity:
|
||||
clientid: "[reference(resourceId(resourceGroup().name, 'Microsoft.ManagedIdentity/userAssignedIdentities/', variables('identityName')), '2018-11-30', 'full').properties.clientId]"
|
||||
- apiVersion: 2018-09-15
|
||||
condition: "[equals(parameters('toggleShutdownSchedule'), 't')]"
|
||||
type: Microsoft.DevTestLab/schedules
|
||||
name: "[concat('shutdown-computevm-', variables('resourcePrefix'))]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
properties:
|
||||
status: Enabled
|
||||
taskType: ComputeVmShutdownTask
|
||||
dailyRecurrence:
|
||||
time: 1900
|
||||
timeZoneId: GMT Standard Time
|
||||
notificationSettings:
|
||||
status: Enabled
|
||||
timeInMinutes: 15
|
||||
emailRecipient: "[parameters('email')]"
|
||||
targetResourceId: "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]"
|
||||
- apiVersion: 2015-06-15
|
||||
# seems to install driver on MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest if there are issues official support is for 2012 / 2016
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/windows/n-series-driver-setup
|
||||
# https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/hpccompute-gpu-windows
|
||||
condition: "[contains(parameters('vmSize'), 'Standard_N')]"
|
||||
type: Microsoft.Compute/virtualMachines/extensions
|
||||
name: "[concat(variables('resourcePrefix'), '/nvidia')]"
|
||||
location: "[parameters('location')]"
|
||||
tags:
|
||||
CFManaged: true
|
||||
Owner: "[if(equals(parameters('email'), 'unused'), parameters('email'), variables('owner'))]"
|
||||
OwnerMail: "[parameters('email')]"
|
||||
Cost Center: "[parameters('projectCode')]"
|
||||
dependsOn:
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
|
||||
- "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'),'/extensions/customscript')]"
|
||||
properties:
|
||||
publisher: Microsoft.HpcCompute
|
||||
type: NvidiaGpuDriverWindows
|
||||
typeHandlerVersion: 1.3
|
||||
autoUpgradeMinorVersion: true
|
||||
settings: {}
|
||||
outputs:
|
||||
utcOutput:
|
||||
type: string
|
||||
value: "[parameters('utcValue')]"
|
||||
adminUsername:
|
||||
type: string
|
||||
value: "[parameters('adminUsername')]"
|
||||
adminPassword:
|
||||
type: string
|
||||
value: "[parameters('adminPassword')]"
|
||||
vmName:
|
||||
type: string
|
||||
value: "[variables('vmName')]"
|
||||
deploymentName:
|
||||
type: string
|
||||
value: "[deployment().name]"
|
||||
ipAddress:
|
||||
type: string
|
||||
value: "[reference(resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))).ipConfigurations[0].properties.privateIPAddress]"
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
#!/bin/bash
|
||||
exec 1> /root/$0.log 2>&1
|
||||
set -x
|
||||
|
||||
# ubuntu much prefers cloud-init
|
||||
# should be rewritten with a common function to wait for apt db and metadata availability with timeouts
|
||||
|
||||
# Let the system init correctly
|
||||
sleep 20
|
||||
|
||||
# stop ssh to ensure no user login during domain join + update, disable this for debug
|
||||
systemctl stop sshd
|
||||
|
||||
# set tz
|
||||
timedatectl set-timezone Europe/London
|
||||
|
||||
# join domain
|
||||
# wait loop until apt database is not locked to avoid clash with Azure policy installs, add a little delay to be able to contact apt repos (probably not Azure ones?)
|
||||
aptupdate=1
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
until [ $aptupdate -eq 0 ];
|
||||
do
|
||||
apt-get -y update
|
||||
apt-get -y install jq
|
||||
which jq
|
||||
aptupdate=$?
|
||||
sleep 5
|
||||
done
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y install krb5-user samba sssd sssd-tools libnss-sss libpam-sss realmd adcli expect
|
||||
|
||||
cat > /etc/krb5.conf <<EOF
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = AD.NOTTINGHAM.AC.UK
|
||||
dns_lookup_realm = true
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
rdns = false
|
||||
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
|
||||
#default_ccache_name = KEYRING:persistent:%{uid}
|
||||
|
||||
[realms]
|
||||
AD.NOTTINGHAM.AC.UK = {
|
||||
kdc = AD.NOTTINGHAM.AC.UK
|
||||
admin_server = AD.NOTTINGHAM.AC.UK
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
.ad.nottingham.ac.uk = AD.NOTTINGHAM.AC.UK
|
||||
EOF
|
||||
|
||||
# password cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# if the password changes this script must be reprocessed and the ARM template updated in the CloudForms orchestration template
|
||||
echo 'As109pHY4Wi9o7naZnhr#!' | kinit -V service_CloudForms@AD.NOTTINGHAM.AC.UK
|
||||
cat > /etc/realmd.conf <<EOF
|
||||
[active-directory]
|
||||
default-client = sssd
|
||||
|
||||
[service]
|
||||
automatic-install = yes
|
||||
|
||||
[ad.nottingham.ac.uk]
|
||||
manage-system = no
|
||||
automatic-id-mapping = yes
|
||||
computer-name = $(hostname -s)
|
||||
computer-ou = ou=AzureCloudForms_POC,ou=Testing,dc=ad,dc=nottingham,dc=ac,dc=uk
|
||||
EOF
|
||||
|
||||
systemctl restart realmd
|
||||
realm join AD.NOTTINGHAM.AC.UK --install=/
|
||||
|
||||
# owner cannot be passed by the ARM template as the whole script is base64 encoded, cloud-init would overcome this limitation
|
||||
# query metadata tag to find owner, this will be used in the sssd.conf whitelist and sudoers
|
||||
owner=$(curl -sH Metadata:true "http://169.254.169.254/metadata/instance?api-version=2019-06-01" | jq .compute.tags | sed 's/\"//g' | awk -F ";" '{for(i=1;i<=NF;i++)if($i~/Owner:/) split($i,result,":");print result[2] }')
|
||||
if [ -z "$owner" ]; then
|
||||
owner=missingtag
|
||||
fi
|
||||
|
||||
cat > /etc/sssd/sssd.conf <<EOF
|
||||
[sssd]
|
||||
domains = ad.nottingham.ac.uk
|
||||
config_file_version = 2
|
||||
services = nss, pam
|
||||
|
||||
[domain/ad.nottingham.ac.uk]
|
||||
ad_domain = ad.nottingham.ac.uk
|
||||
krb5_realm = AD.NOTTINGHAM.AC.UK
|
||||
realmd_tags = joined-with-adcli
|
||||
cache_credentials = True
|
||||
id_provider = ad
|
||||
krb5_store_password_if_offline = True
|
||||
default_shell = /bin/bash
|
||||
ldap_sasl_authid = $(hostname -s)$
|
||||
ldap_id_mapping = True
|
||||
fallback_homedir = /home/%u@%d
|
||||
auth_provider = ad
|
||||
# uon specific with computer account object with no dns
|
||||
use_fully_qualified_names = False
|
||||
enumerate = False
|
||||
# uon large directory performance options
|
||||
ignore_group_members = True
|
||||
entry_cache_timeout = 600
|
||||
# uon search base tuning - target OU for large directory
|
||||
ldap_user_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
#ldap_group_search_base = OU=Users,OU=University,DC=ad,DC=nottingham,DC=ac,DC=uk
|
||||
ldap_use_tokengroups = False
|
||||
# restrict access to owner
|
||||
access_provider = simple
|
||||
simple_allow_users = $owner
|
||||
EOF
|
||||
|
||||
# stop sssd until update finished
|
||||
systemctl stop sssd
|
||||
systemctl enable sssd
|
||||
|
||||
# setup sudoers
|
||||
cat > /etc/sudoers.d/nottingham <<EOF
|
||||
$owner ALL=(ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
#install desktop, mate desktop meta package requires user interaction to select window manager, expect doesnt play well in whatever shell this script is run from - this minimal install is quicker @10mins
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
apt-get -y --no-install-recommends install x2goserver firefox caja compiz-mate engrampa eom folder-color-caja gnome-accessibility-themes gnome-colors-common gnome-icon-theme gnome-orca grub2-themes-ubuntu-mate indicator-messages indicator-power indicator-session indicator-sound lightdm-gtk-greeter mate-core mate-accessibility-profiles mate-applet-appmenu mate-applet-brisk-menu mate-calc mate-desktop mate-dock-applet mate-hud mate-icon-theme mate-menu mate-menus mate-netbook mate-optimus mate-screensaver mate-screensaver-common mate-system-monitor mate-tweak mate-user-guide mate-utils mate-window-applets-common mate-window-buttons-applet mate-window-menu-applet mate-window-title-applet plank plymouth-theme-ubuntu-mate-logo plymouth-theme-ubuntu-mate-text sessioninstaller sound-theme-freedesktop tilda ubuntu-mate-artwork ubuntu-mate-core ubuntu-mate-default-settings ubuntu-mate-guide ubuntu-mate-icon-themes ubuntu-mate-lightdm-theme ubuntu-mate-themes ubuntu-mate-wallpapers* ubuntu-standard
|
||||
|
||||
# enable home directory creation at logon - perform after desktop install to avoid manual prompts with desktop install
|
||||
sed -i 's/^.*pam_sss.so.*$/&\nsession required\tpam_mkhomedir.so skel=\/etc\/skel\/ umask=0077/' /etc/pam.d/common-session
|
||||
|
||||
# add secondary disk
|
||||
parted -s /dev/disk/azure/scsi1/lun0 -- mklabel gpt mkpart primary xfs 0% 100%
|
||||
sleep 2 # allow partition to register
|
||||
mkfs.xfs /dev/disk/azure/scsi1/lun0-part1
|
||||
sleep 2 # allow filesystem creation to finish
|
||||
xfs_admin -L uondata /dev/disk/azure/scsi1/lun0-part1
|
||||
mkdir /uondata
|
||||
echo "LABEL=uondata /uondata xfs defaults 0 0" >> /etc/fstab
|
||||
mount -a
|
||||
chmod 777 /uondata
|
||||
|
||||
# install azcopy tool, wait for managed identity to be ready, init azcopy with managed identity token, download UoN installer packages
|
||||
wget -O azcopy_v10.tar.gz https://aka.ms/downloadazcopy-v10-linux
|
||||
tar -xf azcopy_v10.tar.gz --strip-components=1
|
||||
mv azcopy /usr/local/bin/
|
||||
chmod +x /usr/local/bin/azcopy
|
||||
rm -Rf azcopy*
|
||||
mkdir /root/uon_packages
|
||||
identretry=5 # loop with timeout until managed identity is available
|
||||
msident=1
|
||||
until [ $msident -eq 0 ] || [ $identretry -eq 0 ];
|
||||
do
|
||||
identretry=$((identretry-1))
|
||||
azcopy login --identity
|
||||
msident=$?
|
||||
sleep 5
|
||||
done
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/CarbonBlackClientSetup-linux-v7.0.0.14291.tgz' '/root/uon_packages'
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/CarbonBlackClientSetup-linux-v7.0.0.14291.sh' '/root/uon_packages'
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/sensorsettings.ini' '/root/uon_packages'
|
||||
azcopy copy 'https://extensionartefact.blob.core.windows.net/extensionartefact/SophosInstall.sh' '/root/uon_packages'
|
||||
|
||||
# install carbon black
|
||||
chmod +x /root/uon_packages/CarbonBlackClientSetup-linux-v7.0.0.14291.sh
|
||||
cd /root/uon_packages
|
||||
#while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done # installer script builds apt packages that can get blocked by the nvidia extension
|
||||
./CarbonBlackClientSetup-linux-v7.0.0.14291.sh
|
||||
|
||||
# install sophos antivirus
|
||||
chmod +x /root/uon_packages/SophosInstall.sh
|
||||
./SophosInstall.sh
|
||||
|
||||
# clean up
|
||||
cd /root
|
||||
rm -Rf /root/uon_packages
|
||||
|
||||
# restart services for domain user login
|
||||
systemctl restart sssd
|
||||
systemctl restart sshd
|
||||
|
||||
#Azure extensions dont like a reboot, dont update without a reboot to ensure no system artifacts
|
||||
#apt-get -y upgrade
|
||||
#reboot
|
||||
|
||||
#exit gracefully for waagent
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# ARM template customscript extension command, -user parameter should be the UoN domain user short name, example: ucats OR uizrs
|
||||
# commandToExecute: "[concat('powershell.exe -ExecutionPolicy Unrestricted -File Azure_WindowsServer_customscript.ps1 -user', ' ', variables('owner'))]"
|
||||
|
||||
Param (
|
||||
[string]$user
|
||||
)
|
||||
|
||||
Add-LocalGroupMember -Group 'Remote Desktop Users' -Member $user
|
||||
Add-LocalGroupMember -Group Administrators -Member $user
|
||||
Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask
|
||||
Get-Disk | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel 'data' -Confirm:$false
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
choco install -y firefox googlechrome azcopy10
|
||||
New-Item -ItemType Directory c:\uon_packages
|
||||
azcopy login --identity
|
||||
azcopy copy https://extensionartefact.blob.core.windows.net/extensionartefact/cbsetup.msi c:\uon_packages\
|
||||
azcopy copy https://extensionartefact.blob.core.windows.net/extensionartefact/CarbonBlackClientSetup.exe c:\uon_packages\
|
||||
azcopy copy https://extensionartefact.blob.core.windows.net/extensionartefact/sensorsettings.ini c:\uon_packages\
|
||||
msiexec /q /i c:\uon_packages\cbsetup.msi /L* c:\uon_packages\log.txt COMPANY_CODE=UON
|
||||
Start-Sleep -Second 5 # allow msi to finish displaying log before removing the directory
|
||||
Remove-Item c:\uon_packages -Recurse
|
||||
|
|
@ -0,0 +1,473 @@
|
|||
#!/bin/bash
|
||||
# Stores all important files in a tmp directory, then completely uninstalls all
|
||||
# current versions of cbsensor installed. This will reduce the chance of
|
||||
# conflicts in the future. When all versions of cbsensor are removed this
|
||||
# installer will then install the subsystems of the cbsensor and restore the old
|
||||
# files. If the files are from 6.1.x they will be restored as a zip file,
|
||||
# otherwise they will be installed in place. Then the sensor will be started.
|
||||
#
|
||||
#Globals
|
||||
###############################################################################
|
||||
PKG_NAME=CarbonBlackClientSetup-linux-v7.0.0.14291.tgz
|
||||
PKG_VER=7.0.0.14291
|
||||
APP_DIR=/var/opt/carbonblack/response
|
||||
LOGSDIR=$APP_DIR/log
|
||||
LOG_FILE=$LOGSDIR/install.log
|
||||
OLDLOGSDIR=/var/log/cb/sensor
|
||||
PATH=$PATH:/sbin:/usr/sbin:/bin:/usr/bin
|
||||
RUNNING_KERNEL=$(uname -r)
|
||||
KERNEL_MAJOR_MINOR=$(echo "$RUNNING_KERNEL" | awk '{split($0,a,"."); print a[1]"."a[2]}')
|
||||
PLATFORM_INSTALL_DIR=$APP_DIR/pkgs
|
||||
USE_OLD_META_RPM=0
|
||||
DATE=$(date)
|
||||
INSTALLDIR=$(dirname "$SCRIPT")
|
||||
PKG_PATH=$INSTALLDIR/$PKG_NAME
|
||||
TMPDIR=$(mktemp -d)
|
||||
|
||||
package_manager=""
|
||||
PKG_MAN=""
|
||||
CBDAEMON_PKG=""
|
||||
PKG_EXTENSION=""
|
||||
OS_ID=""
|
||||
VER_ID=""
|
||||
|
||||
USE_EBPF=false
|
||||
USE_SYSV=false
|
||||
USE_SYSD=false
|
||||
USE_KMOD=false
|
||||
|
||||
FLAGS=()
|
||||
SUBSYSTEMS=(cbsensor cbsysd cbebpf cbsysv cbkmod)
|
||||
|
||||
#OS specific stuff
|
||||
OS_INSTALL=""
|
||||
OS_REINSTALL=""
|
||||
OS_DOWNGRADE=""
|
||||
OS_UPGRADE=""
|
||||
|
||||
#Functions
|
||||
###############################################################################
|
||||
check_root() {
|
||||
if [ "$(whoami)" != "root" ]; then
|
||||
print "Please run the install again as root."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Appends '## ' to a string and prints it to the console
|
||||
print() {
|
||||
local string="$1"
|
||||
|
||||
echo "## $string"
|
||||
}
|
||||
|
||||
# Appends '## ' to a string and writes it to the log file
|
||||
log() {
|
||||
local string="$1"
|
||||
|
||||
echo "## $string" >>${LOG_FILE}
|
||||
}
|
||||
|
||||
# Appends '## ' to a string and prints it to the console, as well as writes to
|
||||
# the log file
|
||||
print_and_log() {
|
||||
local string="$1"
|
||||
|
||||
print "$string"
|
||||
log "$string"
|
||||
}
|
||||
|
||||
#Use a temp dir to store useful files to be moved after installation
|
||||
store_saved_files() {
|
||||
#Store and tar important 6.1.x files
|
||||
if [[ -d "$OLDLOGSDIR" ]]; then
|
||||
pushd "$TMPDIR"
|
||||
"$TAR" czvf cbsensor_logs.old.tgz "$OLDLOGSDIR"
|
||||
popd
|
||||
fi
|
||||
}
|
||||
|
||||
restore_saved_files() {
|
||||
cp "$TMPDIR"/cbsensor_logs.old/ $LOGSDIR
|
||||
}
|
||||
|
||||
set_tools() {
|
||||
if [ "$(command -v tar)" ]; then
|
||||
print_and_log "Found tar"
|
||||
TAR=$(command -v tar)
|
||||
fi
|
||||
|
||||
if [ "$(command -v rpm)" ]; then
|
||||
package_manager="rpm"
|
||||
PKG_MAN=$(command -v rpm)
|
||||
|
||||
OS_INSTALL="-ivh --replacefiles"
|
||||
OS_REINSTALL="-ivh --force --replacefiles"
|
||||
OS_DOWNGRADE="-Uvh --oldpackage --replacefiles"
|
||||
OS_UPGRADE="-Uvh --replacefiles"
|
||||
|
||||
REMOVE_PACKAGE() {
|
||||
$($PKG_MAN -e "$1")
|
||||
}
|
||||
|
||||
QUERY_INSTALLED_VERSION() {
|
||||
retval=$($PKG_MAN -qa --queryformat '%{version}' "$1")
|
||||
retval="${retval//v/}"
|
||||
|
||||
# return the version without the 'v'
|
||||
echo "$retval"
|
||||
}
|
||||
QUERY_NEW_VERSION() {
|
||||
retval=$($PKG_MAN -qp --queryformat '%{version}' "$1")
|
||||
retval="${retval//v/}"
|
||||
|
||||
# return the version without the 'v'
|
||||
echo "$retval"
|
||||
}
|
||||
fi
|
||||
|
||||
if [ "$(command -v dpkg)" ]; then
|
||||
package_manager="dpkg"
|
||||
PKG_MAN=$(command -v dpkg)
|
||||
|
||||
OS_INSTALL="-i --force-overwrite"
|
||||
OS_REINSTALL="-i --force-overwrite"
|
||||
OS_DOWNGRADE="-i --force-overwrite"
|
||||
OS_UPGRADE="-i --force-overwrite"
|
||||
|
||||
REMOVE_PACKAGE() {
|
||||
$($PKG_MAN --purge "$1")
|
||||
}
|
||||
|
||||
QUERY_INSTALLED_VERSION() {
|
||||
retval=$($PKG_MAN -s "$1" 2> /dev/null | grep -w Version | awk -F": " '{print $2}')
|
||||
retval="${retval//v/}"
|
||||
|
||||
# return the version without the 'v'
|
||||
echo "$retval"
|
||||
}
|
||||
QUERY_NEW_VERSION() {
|
||||
retval=$(dpkg-deb --info "$1" | grep Version | awk -F ": " '{print $2}')
|
||||
retval="${retval//v/}"
|
||||
|
||||
# return the version without the 'v'
|
||||
echo "$retval"
|
||||
}
|
||||
fi
|
||||
|
||||
print_and_log "Using $package_manager as package manager"
|
||||
}
|
||||
|
||||
check_kernel_devel() {
|
||||
if [ "$USE_EBPF" = true ]; then
|
||||
if [ "$OS_ID" = "ubuntu" ]; then
|
||||
kernel_devel=$(dpkg --list | grep linux-headers-generic)
|
||||
|
||||
if [ ${kernel_devel} -eq 1 ]; then
|
||||
print_and_log "The package 'linux-headers-generic' is not installed. Please install kernel-devel."
|
||||
fi
|
||||
else
|
||||
kernel_devel=$(rpm -qa | grep kernel_devel)
|
||||
|
||||
if [ ${kernel_devel} -eq 1 ]; then
|
||||
print_and_log "The package 'kernel_devel' is not installed. Please install kernel-devel."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
determine_os() {
|
||||
if [[ -e /etc/os-release ]]; then
|
||||
OS_ID=$(grep -w "ID" </etc/os-release | awk -F= '{print $2}' | tr -d '"' | tr -d '[:space:]')
|
||||
elif [[ -e /etc/redhat-release ]]; then
|
||||
OS_ID=$(cut -d" " -f1 /etc/redhat-release)
|
||||
elif [[ -e /etc/system-release ]]; then
|
||||
OS_ID=$(cut -d" " -f1 /etc/system-release)
|
||||
fi
|
||||
|
||||
# Convert OS_ID to all lowercase
|
||||
OS_ID=${OS_ID,,}
|
||||
|
||||
#account for rhel6 "red hat"
|
||||
if [ "$OS_ID" = "red" ]; then
|
||||
OS_ID="redhat"
|
||||
fi
|
||||
}
|
||||
|
||||
set_flags() {
|
||||
if ls -l /proc/1/exe | grep -w systemd >>/dev/null 2>&1; then
|
||||
USE_SYSD=true
|
||||
else
|
||||
USE_SYSV=true
|
||||
fi
|
||||
|
||||
# If the kernel is 4.4 or later use eBPF.
|
||||
if version_lt "$KERNEL_MAJOR_MINOR" "4.4"; then
|
||||
USE_KMOD=true
|
||||
else
|
||||
USE_EBPF=true
|
||||
fi
|
||||
|
||||
# If the platform is ubuntu then change the extension
|
||||
if [ "$OS_ID" = "ubuntu" ]; then
|
||||
PKG_EXTENSION="deb"
|
||||
else
|
||||
PKG_EXTENSION="rpm"
|
||||
fi
|
||||
|
||||
CBDAEMON_PKG="cbsensor-CBDAEMON-$PKG_VER-1.x86_64.$PKG_EXTENSION"
|
||||
|
||||
FLAGS+=("$CBDAEMON_PKG")
|
||||
|
||||
if [ "$USE_EBPF" = true ]; then
|
||||
FLAGS+=("cbsensor-EBPF-${PKG_VER}-1.x86_64.$PKG_EXTENSION")
|
||||
fi
|
||||
|
||||
if [ "$USE_SYSV" = true ]; then
|
||||
FLAGS+=("cbsensor-SYSV-${PKG_VER}-1.x86_64.$PKG_EXTENSION")
|
||||
fi
|
||||
|
||||
if [ "$USE_SYSD" = true ]; then
|
||||
FLAGS+=("cbsensor-SYSD-${PKG_VER}-1.x86_64.$PKG_EXTENSION")
|
||||
fi
|
||||
|
||||
if [ "$USE_KMOD" = true ]; then
|
||||
FLAGS+=("cbsensor-KMOD-${PKG_VER}-1.x86_64.$PKG_EXTENSION")
|
||||
fi
|
||||
}
|
||||
|
||||
version_gt() {
|
||||
test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"
|
||||
}
|
||||
|
||||
version_lt() {
|
||||
test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"
|
||||
}
|
||||
|
||||
version_ge() {
|
||||
test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1";
|
||||
}
|
||||
|
||||
perform_operation() {
|
||||
print_and_log "############################################################################"
|
||||
log "$DATE"
|
||||
print_and_log "Installing cbsensor from $INSTALLDIR"
|
||||
|
||||
# Figure out the currently installed version and the new version to install.
|
||||
oVersion=$(QUERY_INSTALLED_VERSION cbsensor)
|
||||
nVersion=$(QUERY_NEW_VERSION "$PLATFORM_INSTALL_DIR"/"$CBDAEMON_PKG")
|
||||
|
||||
# The default operation is to install
|
||||
op="--install"
|
||||
op_txt="install"
|
||||
|
||||
# if no old version, then this is a clean install
|
||||
if [ "$oVersion" = "" ]; then
|
||||
op=${OS_INSTALL}
|
||||
op_txt="install"
|
||||
|
||||
# If the new version is the same as the installed version we need a reinstall
|
||||
elif [ "$oVersion" = "$nVersion" ]; then
|
||||
op=${OS_REINSTALL}
|
||||
op_txt="reinstall"
|
||||
# If the installed version is older than the new version we need an upgrade
|
||||
elif version_lt "$oVersion" "$nVersion"; then
|
||||
op=${OS_UPGRADE}
|
||||
op_txt="upgrade"
|
||||
# If the new version is older than the installed version we need a downgrade
|
||||
else
|
||||
version_gt "$oVersion" "$nVersion"
|
||||
op=${OS_DOWNGRADE}
|
||||
op_txt="downgrade"
|
||||
fi
|
||||
|
||||
if [[ "$oVersion" == "" ]]; then
|
||||
print_and_log "$op_txt $nVersion"
|
||||
else
|
||||
print_and_log "$op_txt $oVersion -> $nVersion"
|
||||
fi
|
||||
|
||||
for pkg in "${FLAGS[@]}"; do
|
||||
# UoN change to wait for locked package manager, other agent and extensions will lock dkpg
|
||||
while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 5; done
|
||||
${PKG_MAN} $op ${PLATFORM_INSTALL_DIR}/"$pkg" | tee -a ${LOG_FILE}
|
||||
done
|
||||
}
|
||||
|
||||
# servers older than 6.2.2 will deploy the legacy meta rpm instead oF the newer tgz file
|
||||
# we will need to handle this case here
|
||||
check_pkg() {
|
||||
if [[ ! -f ${PKG_PATH} ]]; then
|
||||
BASE_NAME=$(basename ${PKG_NAME} .tgz)
|
||||
PKG_NAME=${BASE_NAME}.rpm
|
||||
USE_OLD_META_RPM=1
|
||||
PKG_PATH=${INSTALLDIR}/${PKG_NAME}
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks for multiple sensors installed. If more than one sensor is installed, the newest is kept and the others are removed.
|
||||
# This only needs to be checked on rpm based systems, dpkg does not allow this to happen.
|
||||
check_duplicate_sensors() {
|
||||
print_and_log "Checking for duplicate sensors"
|
||||
hasDups=$($PKG_MAN -qa | grep cbsensor | awk '{ a=1-a; if(!a) print $0 }')
|
||||
if [ -n "$hasDups" ]; then
|
||||
dups=$($PKG_MAN -qa --queryformat '%{version} ' cbsensor 2>/dev/null)
|
||||
print_and_log "Duplicate sensors detected: $dups"
|
||||
print_and_log "Attempting to remove $hasDups"
|
||||
($PKG_MAN -e --nodeps "$hasDups" >>$LOG_FILE 2>&1)
|
||||
print_and_log "############################################################################"
|
||||
fi
|
||||
}
|
||||
|
||||
add_daemon() {
|
||||
print_and_log "Adding daemon..."
|
||||
if [ "$USE_SYSD" = true ]; then
|
||||
print_and_log "$(systemctl enable cbdaemon.service 2>&1)"
|
||||
if [ "$USE_EBPF" = true ]; then
|
||||
print_and_log "$(systemctl enable cbebpfdaemon.service 2>&1)"
|
||||
fi
|
||||
else
|
||||
print_and_log "$(chkconfig --add cbdaemon 2>&1)"
|
||||
fi
|
||||
|
||||
if [ "$USE_EBPF" = false ]; then
|
||||
print_and_log "$(sh /etc/sysconfig/modules/cbresponse.modules 2>&1)"
|
||||
fi
|
||||
|
||||
print_and_log "$(chmod 600 /var/opt/carbonblack/response 2>&1)"
|
||||
|
||||
print_and_log "Starting daemon..."
|
||||
if [ "$USE_SYSD" = true ]; then
|
||||
print_and_log "$(systemctl restart cbdaemon.service 2>&1)"
|
||||
if [ "$USE_EBPF" = true ]; then
|
||||
print_and_log "$(systemctl restart cbebpfdaemon.service 2>&1)"
|
||||
fi
|
||||
else
|
||||
#Fix for reinstalling the package on a system with sysV
|
||||
if pgrep cbdaemon >/dev/null 2>&1; then
|
||||
print_and_log "$(/etc/init.d/cbdaemon restart 2>&1)"
|
||||
else
|
||||
print_and_log "$(/etc/init.d/cbdaemon start 2>&1)"
|
||||
fi
|
||||
fi
|
||||
|
||||
print_and_log "CB Response Sensor is now installed"
|
||||
}
|
||||
|
||||
check_necessary_files() {
|
||||
if [[ ! -f "$PKG_PATH" ]]; then
|
||||
print_and_log "Error: Installation Package is not present"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! (-f "$INSTALLDIR"/sensorsettings.ini) ]]; then
|
||||
print_and_log "Error: sensorsettings.ini file is not present"
|
||||
exit 2
|
||||
else
|
||||
cp "$INSTALLDIR"/sensorsettings.ini "$APP_DIR"
|
||||
chmod 644 "$APP_DIR"/sensorsettings.ini
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
check_meta_rpm() {
|
||||
# Make sure the older meta package is no longer present from previous installations
|
||||
set +e # If the RPM isn't installed, we expect to catch an error in rval. Don't abort on error.
|
||||
CB_RPM_UNIST=("cb-linux-sensor" "cb_package-UNIFIED_RPM")
|
||||
for val in "${CB_RPM_UNIST[@]}"; do
|
||||
${RPM} -q "$val" >/dev/null 2>/dev/null
|
||||
rval=$?
|
||||
if [[ ${rval} -eq 0 ]]; then
|
||||
echo "## Remove old meta rpm: "$val"" | tee -a ${LOG_FILE}
|
||||
${RPM} -e "$val" >>${LOG_FILE} 2>&1
|
||||
echo "############################################################################" | tee -a ${LOG_FILE}
|
||||
fi
|
||||
done
|
||||
set -e # Re-enable abort on error.
|
||||
}
|
||||
|
||||
# Install using tarball or legacy meta RPM
|
||||
extract_package() {
|
||||
print_and_log "Extracting package"
|
||||
if [[ ${USE_OLD_META_RPM} == 0 ]]; then
|
||||
rm -f "${INSTALLDIR}"/*.rpm # Remove any old RPMs first
|
||||
(${TAR} xvzf "${PKG_PATH}" -C ${PLATFORM_INSTALL_DIR} | tee -a ${LOG_FILE})
|
||||
else
|
||||
(${RPM} -i "${PKG_NAME}" >>${LOG_FILE})
|
||||
fi
|
||||
|
||||
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
||||
print_and_log "install failed on package extraction"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
build_directory_structure() {
|
||||
mkdir -p "$LOGSDIR"
|
||||
touch $LOG_FILE
|
||||
print_and_log "Building directory structure"
|
||||
mkdir -p "$APP_DIR"/eventlogs/finalized
|
||||
mkdir -p "$APP_DIR"/pkgs
|
||||
mkdir -p "$APP_DIR"/store
|
||||
}
|
||||
|
||||
# Make sure the older meta package is no longer present from previous installations
|
||||
remove_old_meta_package() {
|
||||
CB_RPM_UNIST=("cb-linux-sensor" "cb_package-UNIFIED_RPM")
|
||||
for val in "${CB_RPM_UNIST[@]}"; do
|
||||
rpm -q "$val" >/dev/null 2>/dev/null
|
||||
rval=$?
|
||||
if [[ ${rval} -eq 0 ]]; then
|
||||
print_and_log "Removing old meta rpm"
|
||||
(${RPM} -e "$CB_RPM_UNIST" >>${LOG_FILE} 2>&1)
|
||||
print_and_log "############################################################################"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
print_and_log "Cleanup setup packages"
|
||||
for file in "${PLATFORM_INSTALL_DIR}"/*; do
|
||||
if [[ -e "$file" ]]; then
|
||||
rm -f ${PLATFORM_INSTALL_DIR}/*
|
||||
fi
|
||||
done
|
||||
|
||||
#
|
||||
# Delete the old logs directory
|
||||
#
|
||||
if [[ -d ${OLDLOGSDIR} ]]; then
|
||||
rm -rf ${OLDLOGSDIR}
|
||||
fi
|
||||
}
|
||||
|
||||
# 6.1.x and earlier versions of the sensor stored data in /var/lib/cb
|
||||
# Since we are upgrading to a new file structure, delete the old files structure
|
||||
# Leaving it in place causes problems with future upgrades.
|
||||
# We make no claims regarding the ability to downgrade from 6.2.x to 6.1.x or earlier.
|
||||
rm -rf /var/lib/cb
|
||||
|
||||
main() {
|
||||
check_root
|
||||
build_directory_structure
|
||||
check_necessary_files
|
||||
check_pkg
|
||||
set_tools
|
||||
determine_os
|
||||
set_flags
|
||||
extract_package
|
||||
#Temp fix until ubuntu stuff is added
|
||||
if [ "$package_manager" = "rpm" ]; then
|
||||
check_duplicate_sensors
|
||||
fi
|
||||
perform_operation
|
||||
add_daemon
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
trap cleanup EXIT
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -0,0 +1,29 @@
|
|||
[CB]
|
||||
ConfigName=LIVE
|
||||
CollectStoreFiles=1
|
||||
CollectModuleLoads=1
|
||||
CollectModuleInfo=1
|
||||
CollectFileMods=1
|
||||
CollectRegMods=1
|
||||
CollectNetConns=1
|
||||
CollectProcesses=1
|
||||
CollectCrossProcess=1
|
||||
CollectEmetEvents=1
|
||||
CollectProcessUserContext=1
|
||||
CollectDataFileWrites=1
|
||||
SensorBackendServer=https%3A//sensors.fit-turtle.my.cbcloud.de%3A443
|
||||
SensorClientCert=-----BEGIN%20CERTIFICATE-----%0AMIICvjCCAaagAwIBAgIQBDVK9y8pT2uelTCFmUI5KjANBgkqhkiG9w0BAQsFADAR%0AMQ8wDQYDVQQDDAZzZXJ2ZXIwHhcNMTkwNDIzMTAzMTU3WhcNMjkwNDIwMTAzMTU3%0AWjAlMSMwIQYDVQQDDBpTZW5zb3IgR3JvdXBbMl06ICdTdGFnaW5nJzCCASIwDQYJ%0AKoZIhvcNAQEBBQADggEPADCCAQoCggEBALphRJO9g%2Bcm8Ezv7SZE9L98xImGEPj%2B%0A4uPMF2kvHl/VG/NjvZN7ihrRELGlTBCwlEaTIMuJflQiXhCkU9qW9T89dO%2BLcoaE%0AxNt23drEjhvJufOn4JrXt72foWPrkiu80x2HzI/5AxfNgohvcu%2By1posEH/5ggiy%0ALgksLTi9jZR9div7BaecbIrI8OGnYNoSrc4vicxpRWTIDOZ3qmOqZVD/%2B0TcQ6DT%0Am0dXD9SETqmghOg7J0mgLZfWUM1bSdQnQVi5AeeAtASF824n71DeHYBTH1oR5Tib%0AREIijBsXd7f7R8YjfWs%2BYbWN5ajTcC5xmYwzhthHeQOJb4Irjk5e4pUCAwEAATAN%0ABgkqhkiG9w0BAQsFAAOCAQEAdtJMzYbjHzLwRRn8gwtD3PAc3Lc9ZyrVEDLoAwXo%0A/ZpSmN9%2BpFz6L084TSn3AVs1zThmSdFEJ9z4hviggPR1P8/XQIH3XJwioK79vg/x%0AQ3SUNbwbJeDaAQTkWcWQXQmH/LNdwB/G3sKkaOydN3QDScuncCsVdjnC2WDGNF8I%0A4zbdsZVHwx6%2Brkr129yTZIaB7xRmE/SP6sY0enfVIG8t20t1QMjhTa6hUG/jEWsv%0ANUYO5bNzXND5GcT8t8goBb8OYEMN0e8peg3UKXP3snzbeMh3WxQywV3J7xusC2vC%0AlujdVS5hchWRiwwNB7QN8nzilbTPDbb9Fm/ffZ9yDrZQAQ%3D%3D%0A-----END%20CERTIFICATE-----%0A
|
||||
SensorClientKey=-----BEGIN%20PRIVATE%20KEY-----%0AMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6YUSTvYPnJvBM%0A7%2B0mRPS/fMSJhhD4/uLjzBdpLx5f1RvzY72Te4oa0RCxpUwQsJRGkyDLiX5UIl4Q%0ApFPalvU/PXTvi3KGhMTbdt3axI4bybnzp%2BCa17e9n6Fj65IrvNMdh8yP%2BQMXzYKI%0Ab3LvstaaLBB/%2BYIIsi4JLC04vY2UfXYr%2BwWnnGyKyPDhp2DaEq3OL4nMaUVkyAzm%0Ad6pjqmVQ//tE3EOg05tHVw/UhE6poIToOydJoC2X1lDNW0nUJ0FYuQHngLQEhfNu%0AJ%2B9Q3h2AUx9aEeU4m0RCIowbF3e3%2B0fGI31rPmG1jeWo03AucZmMM4bYR3kDiW%2BC%0AK45OXuKVAgMBAAECggEAawbkHHrdveVsZKH6atl6OmPpcJeeM34ayHkwkGRQavOz%0Aw0ZpXMG6gr%2B/eGPVlFfyLbzbPkZMVwANSD01MfcyCgi%2Bl7haIckoSmat66yndmmW%0A8MZcgk6R4sBCK2DhZWBfUzviSmLSVd7bsIFfXSozdgEL0JF1DI1VRksqBMVFAhk1%0ArEbnfIm9MD5HHhAl3WaAVqlTaFYU0SK7%2BrM7Eb4UtQyBGLpnfNLdB6L7C0a8MJSp%0Ao1cIT92HROozysGTZUrUinALsuEObz43FSjIjXknyBZKoPP5Cq8Z4Hy9nZEV9eRA%0ATKAfIt5L82F0xu1Mu0Wfb8TEHTOvUd0WW/93Ot31GQKBgQDw0H838gNQP/U6GRav%0AlQTZkE68j4bNs/fNmDp4tTG3CAOmjHslInzUwf9XwgRyZcJCkJsj1tTpfJUsSPA/%0AFuOmSQXIwMy/4q2eo1hCJPauaom7tGxOxF7elrl/8swfnyXRD6uNH%2BUlZdaAu2b4%0A7wxRSQzPYrzdy4oqkQC85sSWIwKBgQDGIgbmWe9MW55tvMZgjlcUIppbIyleFAyh%0AjJTL4mgKBxN/sGyLrTfrxGaELp81lbHdjvpkWUXLyIcbXk1Qigpwl6XnPpZzHdq8%0Arp%2BIA%2Bh8jxX6nDN4qzmopf3QfCmihZMuI6S9mK6BIFJS3xH4M/AkDPnSZY6oL%2BZK%0AHjou1vMD5wKBgA5ys%2B9LWd%2BKts/RiYbnTe8vtUmi4tecoJV5OKjdVipBFNb9PrYv%0Ap4WsTgGZ5mJmsI0F2AkCbqvDib%2BqcJ%2BYY/gqEWrGBoLMutX1vunZBePZCIJ2hLkF%0AFxwLtv1yN8T962rrocNJ0pm3znLSy47L6NVHqLkYl3RHLfb31C%2BBOPI5AoGAMNlm%0A1hhKVYspLmkih9/QPFijseCjbFyJGLNuZC439HD2L78xo%2BZbKTfNBr3v5ug3aCa%2B%0AOUuBV9Li7K59ZWQDWusorjDSKyOrMGXlU0WTZlJo2tb0IcYlex0hzOsv4LAKL4/%2B%0AJ/ii3Zc4dNImvgkgJeNFHMiJOZJFtvRo5%2B97DvUCgYB7gbPFXAnUfSfad3hmnHWS%0AYmX609o47kwsQ4YjOGENYyU/NPVhbqWKRxHkVkaSIIvbCLcgZ9M8UPgiYUf2kvt6%0APqcQJ72ktgf5HzBxTveCc45xn15Cffu9N7EV4FnwcRqWtUcpXQVJYk1fObKL2eL5%0AG9v3zLBL6eKb9CAul8tapA%3D%3D%0A-----END%20PRIVATE%20KEY-----%0A
|
||||
CbServerCert=-----BEGIN%20CERTIFICATE-----%0AMIICnjCCAYagAwIBAgIEXKFjwTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZz%0AZXJ2ZXIwHhcNMTkwNDAxMDEwNTA1WhcNMjkwMzI5MDEwNTA1WjARMQ8wDQYDVQQD%0ADAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCu1ygbPU5f%0AMPFSBugTrZnguPpwizJvUyTrIteuzN4NWKhIH05VeG49ghypFiEDOk31IfN5%2Bj1s%0AP8bYsj5U0TYrE4nG%2BMJwCFEroj%2B/KrIk5kuak69m15MrRagdYdJe5ev4wcIGcyDV%0A9dvXN0qBoWxL%2BK9oBrdXKBrHWx8PkL9Q8Zd8EvJ90QhXG4l1UiGAVM968gUUV%2Bmx%0AeMfONhKgW3cQD0R6QcR7PwjRMOLNeNDvbcSnpP%2BEhY3BGAUGjzgriLGrYotW0/Aq%0Ak%2B4yXeMtd1fxq0C8zaFgqrW/NKahgmifzLSZd034j74oCa2U9jffthWe2UFgUhcd%0AJF0xl4ilzQzjAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEgll3nCYKIoGhzIJbgO%0A4Y9yOUOTsQ/fKgbj5%2BKXy%2BLMnues3feF3uLQ0vaEl4BE/%2BJbI0cGo27fTNz/I7Wk%0ABp6WSHyBeV93WwRTNyeIjW0ytVual9i5d04/M38ksvKHmzep9P3i%2BJ%2BWYSAanDCG%0A0gHTyS3NKrYSONOF5vCn5M0LucCWVd77ziOChcHi1/kO1%2BHm4hbRv7PldVeS8C8/%0ArQvuq7DFPESqtMOX%2BTMLT4CcdiI%2BjQ0HZhoU4YirEp53WGFAixuq0e3ARS/VGK4K%0AF14A7DlzTYm84u8Ru7J/cgtrHUHUB5%2BrzzYfV1KyjT8hLwokB6To9y7jni%2BLBdq4%0Aj60%3D%0A-----END%20CERTIFICATE-----%0A
|
||||
MaxLicenses=-1
|
||||
SensorVersion=Manual
|
||||
QuotaEventlogPercent=1
|
||||
QuotaEventlogBytes=536870912
|
||||
QuotaStorefileBytes=536870912
|
||||
QuotaStorefilePercent=1
|
||||
VdiEnabled=0
|
||||
TamperLevel=0
|
||||
SensorExeName=
|
||||
ProcessFilterLevel=1
|
||||
FilterKnownDLLs=1
|
||||
CbServerSSLCertStrictCheck=0
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# What is this?
|
||||
|
||||
A collection of parametrized (linux/windows/GPU) ARM templates used to self service provision various flavours of research VMs.
|
||||
The ARM templates slot into a Azure cloud infrastructure designed like an on-premesis network (not sure why), as such the templates join the VMs to a classic Windows Active Directory apply strict policies and install corporate spyware fromm private blobs, a sort of limitless hypervisor.
|
||||
|
||||
A collection of parameterized Ansible playbooks designed to run as scripts for:
|
||||
|
||||
allow end users to add themselves to various AD groups for service access (ansible running winrm and remote powershell)
|
||||
allow end users to provision SAMBA shares from various storage platforms, either corporate or HPC (ansible URI API calls)
|
||||
|
||||
## This repo contains the assets used in the cloudforms self service project
|
||||
|
||||
| Template | Description |
|
||||
|--|--|
|
||||
| ansible-ad-group-add | Adds AD users to AD groups, with self service logic and email notification, parameterised for reuse with different groups and email templates. |
|
||||
| ansible-gpfs-samba-provision | Creates GPFS samba shares, ready to be parameterised for cloudforms, not in production, emails not pretty for customers. |
|
||||
| ansible-netapp-qtree-provison | Creates qtrees with AD ACL's on a target volume shared via samba, ready to be parameterised for cloudforms, not in production, no emails. |
|
||||
| ARM_templates/prod_1.0 | ARM templates to deploy Azure instances and associated networking in different prod RG's, include scripts and extensions that join UoN AD. |
|
||||
| ARM_templates/prod_1.1 | ARM templates to deploy Azure instances and associated networking in different prod RG's, include storage container access to pull installer scripts/packages, broadly joining domain, installing desktop components, formatting disk, setting permissions and installing carbon black. |
|
||||
| ARM_templates/dev | ARM templates to deploy Azure instances in dev RG |
|
||||
|
||||
Please refer to the README.md in each folder for usage and further details.
|
||||
|
||||
## Included playbooks and ARM templates
|
||||
|
||||
├── ansible-ad-group-add
|
||||
│ ├── templates
|
||||
│ │ ├── default
|
||||
│ │ └── transcription
|
||||
│ └── vars
|
||||
├── ansible-gpfs-samba-provision
|
||||
│ ├── tasks
|
||||
│ ├── templates
|
||||
│ └── vars
|
||||
├── ansible-netapp-cifs-provision
|
||||
│ └── vars
|
||||
├── ansible-netapp-qtree-provison
|
||||
│ ├── templates
|
||||
│ └── vars
|
||||
└── ARM_templates
|
||||
├── dev
|
||||
│ ├── rev1
|
||||
│ ├── rev2
|
||||
│ └── rev3
|
||||
├── prod_1.0
|
||||
└── prod_1.1
|
||||
└── extensionartefacts
|
||||
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
# Usage
|
||||
|
||||
|
||||
|
||||
Should be run from a Cloudforms tile with parameters passed on runtime, there should be no need to edit the variables main.yml.
|
||||
|
||||
|
||||
|
||||
Cloudforms tiles pass the following mandatory parameters:
|
||||
|
||||
|
||||
|
||||
groupmembers="user" / "user1,user2,userN"
|
||||
|
||||
group=ad-group
|
||||
|
||||
perform=create / delete
|
||||
|
||||
ad_host=fqdn-of-ad-server
|
||||
|
||||
ad_user="user" / "user@DOMAIN.COM"
|
||||
|
||||
ad_pass="account-password"
|
||||
|
||||
from_email=donotreply@nottingham.ac.uk
|
||||
|
||||
api_user=cloudforms-user-with-api-write
|
||||
|
||||
api_pass=cloudforms-user-pass
|
||||
|
||||
|
||||
|
||||
- ad_pass / api_pass should have value in quotes to allow any special characters, all parameter accept quoted
|
||||
|
||||
- ad_user must have sufficient rights to administer group and query user object properties
|
||||
|
||||
- email_requester passed by cloudforms using API
|
||||
|
||||
- from_email address should be allowed through relay whitelist
|
||||
|
||||
|
||||
|
||||
More parameters can be passed, check vars/main.yml for all functional parameters, these include an output toggle, winrm connectivity and email toggles.
|
||||
|
||||
|
||||
|
||||
It is not recommended to hard code sensitive parameters such as passwords in the playbook vars/main.yml, these should be stored in Cloudforms which encrypts these in the database.
|
||||
|
||||
|
||||
|
||||
## Failure conditions
|
||||
|
||||
|
||||
|
||||
Any items in vars/main.yml that have the value placeholder that are not passed as parameters on runtime will fail the playbook.
|
||||
|
||||
|
||||
|
||||
Invalid users that are not in ActiveDirectory.
|
||||
|
||||
|
||||
|
||||
Connectivity issues with ActiveDirectory or SMTP relay. Ansible module error output will be observed in these scenarios.
|
||||
|
||||
|
||||
|
||||
## Note
|
||||
|
||||
|
||||
|
||||
Parameters can be passed in any order.
|
||||
|
||||
|
||||
|
||||
The groupmembers field will validate only populated comma delimited entries, the following will be accepted:
|
||||
|
||||
|
||||
|
||||
",,,tseed,,swright,,"
|
||||
|
||||
|
||||
|
||||
ActiveDirectory user objects have associated email addresses pulled from their published properties. Job updates will be sent to these email accounts.
|
||||
|
||||
|
||||
|
||||
Failure emails where ActiveDirectory users are invalid are only sent to the requester.
|
||||
|
||||
|
||||
|
||||
## Example command to run playbook on the command line
|
||||
|
||||
|
||||
|
||||
ansible-playbook adgroup.yml -e 'groupmembers="tseed,swright" \
|
||||
|
||||
group=Project \
|
||||
|
||||
perform=delete \
|
||||
|
||||
ad_host=WIN-1JE0R5GCBSG.NETAPPSIM.LOCAL \
|
||||
|
||||
ad_user="administrator@NETAPPSIM.LOCAL" \
|
||||
|
||||
ad_pass="Password0" \
|
||||
|
||||
from_email=tseed@ocf.co.uk \
|
||||
|
||||
enable_requester_email=false \
|
||||
|
||||
enable_customer_email=false \
|
||||
|
||||
api_user=dummy \
|
||||
|
||||
api_pass=dummy'
|
||||
|
||||
|
||||
|
||||
## Example command to run playbook on the command line and send emails with custom customer email templates
|
||||
|
||||
|
||||
|
||||
ansible-playbook adgroup.yml -e 'groupmembers="tseed,swright" \
|
||||
|
||||
group=Project \
|
||||
|
||||
perform=delete \
|
||||
|
||||
ad_host=WIN-1JE0R5GCBSG.NETAPPSIM.LOCAL \
|
||||
|
||||
ad_user=administrator \
|
||||
|
||||
ad_pass="Password0" \
|
||||
|
||||
from_email="noreply@cloudforms" \
|
||||
|
||||
enable_requester_email=true \
|
||||
|
||||
enable_customer_email=true \
|
||||
|
||||
smtp_relay=192.168.101.240 \
|
||||
|
||||
smtp_port=25 \
|
||||
|
||||
template_prefix=transcription \
|
||||
|
||||
requester_email=tseed@ocf.co.uk \
|
||||
|
||||
api_user=dummy \
|
||||
|
||||
api_pass=dummy '
|
||||
|
||||
|
||||
|
||||
## Self service mode
|
||||
|
||||
|
||||
|
||||
The initial design of the script catered for user(s) being added to a group and the requester getting status emails for add/remove/invalid-user/no-change operations and the users to receive add/remove emails.
|
||||
|
||||
|
||||
|
||||
An updated use case where the requester user populates the groupmembers parameter only with its own username is known as the self service model.
|
||||
|
||||
This model effectively disables the requester status emails and will send add/remove/no-change user emails to the requester.
|
||||
|
||||
|
||||
|
||||
To replicate this behaviour on the command line ensure the following parameter is passed with the above example syntax, and ensure the groupmember parameter only has a single user account entry.
|
||||
|
||||
|
||||
|
||||
groupmembers="tseed"
|
||||
|
||||
spoof_self_service=true
|
||||
|
||||
|
||||
|
||||
## Email behaviour
|
||||
|
||||
When template_prefix is omitted the value is set to default and default email templates used, in this scenario customer emails are not sent disregarding the parameter enable_customer_email.
|
||||
|
||||
To add service specific emails, create a new directory under the templates directory, populate email templates and pass matching parameter template_prefix=<my_new_service>.
|
||||
|
||||
Requester email templates are prefixed mail-, customer email templates customer-, there are 4 conditions in which templates are suffixed - add / remove / invalid / nochange.
|
||||
|
||||
Name the email templates in accordance with this convention, e.g mail-add.j2.
|
||||
|
||||
Customers will receive an add / remove / no-change email only when an action has been performed upon their account, customer emails are in html format.
|
||||
|
||||
Cloudforms local accounts such as admin do not have an email address, to debug with said accounts add parameter requester_email=<your uon email>
|
||||
|
|
@ -0,0 +1,550 @@
|
|||
---
|
||||
- name: Query automate workspace
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- set_fact:
|
||||
endpoint: "{{ manageiq.api_url }}"
|
||||
auth_token: "{{ manageiq.api_token }}"
|
||||
request_id: "{{ (manageiq.request).split('/')[-1] }}"
|
||||
service_id: "{{ (manageiq.service).split('/')[-1] }}"
|
||||
user_id: "{{ (manageiq.user).split('/')[-1] }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# when users run ansible their manageiq auth token does not have sufficient rights to interact with the API, no combination of rights in a role for a non admin user are sufficient
|
||||
# use the local admin credentials to get an auth token
|
||||
- name: Get auth token
|
||||
uri:
|
||||
# ansible runner timeout, something changed from 5.11.1.2 to 5.11.6.0?
|
||||
#url: "{{ endpoint }}/api/auth"
|
||||
url: "https://127.0.0.1/api/auth"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
user: "{{ api_user }}"
|
||||
password: "{{ api_pass }}"
|
||||
status_code: 200
|
||||
register: login
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
auth_token: "{{ login.json.auth_token }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- name: Get requester user attributes
|
||||
uri:
|
||||
#url: "{{ endpoint }}/api/users/{{ user_id }}"
|
||||
url: "https://127.0.0.1/api/users/{{ user_id }}"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
status_code: 200
|
||||
register: user
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
requester_email: "{{ user.json.email }}"
|
||||
when: manageiq is defined and user.json.email is not none # cloudforms admin user has no email address (unless set), this is a null field in json
|
||||
|
||||
- set_fact:
|
||||
requester_email: "{{ from_email }}" # from_email should be able to recieve mail via relay
|
||||
when: requester_email is not defined
|
||||
|
||||
- set_fact:
|
||||
requester_user: "{{ user.json.name }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# when run from the command line set a default requester user
|
||||
- set_fact:
|
||||
requester_user: "command line invocation"
|
||||
when: manageiq is not defined
|
||||
|
||||
# - name: DEBUG print all user attributes
|
||||
# debug:
|
||||
# msg:
|
||||
# - "{{ user }}"
|
||||
#
|
||||
# useful fields:
|
||||
# "email": "ucats@exmail.nottingham.ac.uk"
|
||||
# "name": "Toby Seed"
|
||||
# "userid": "toby.seed@nottingham.ac.uk"
|
||||
#
|
||||
# we see that the UON active directory schema has a different correlation between the AD short login id and the lookup of the userid and name
|
||||
# the above user generally would use the login id "ucats" across the UON estate to authenticate against AD
|
||||
# interestingly cloudforms queries several fields in the schema and will allow login as "ucats" and "toby.seed@nottingham.ac.uk"
|
||||
# to detect if this script is being run in self service mode, a match is performed against the requesting user and a single entry in groupmember list
|
||||
# we can only retrieve the expected AD short login id from the email field using UON AD
|
||||
|
||||
# remove after testing - self service mode wasnt tested enough on dev system where cloudforms admin has no email
|
||||
# - name: get AD account name
|
||||
# set_fact:
|
||||
# requester_user_ad: "{{ (user.json.email).split('@')[0] }}"
|
||||
# when: manageiq is defined
|
||||
|
||||
- name: get AD account name
|
||||
set_fact:
|
||||
requester_user_ad: "{{ (requester_email).split('@')[0] }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- name: get service
|
||||
uri:
|
||||
#url: "{{ endpoint }}/api/services/{{ service_id }}"
|
||||
url: "https://127.0.0.1/api/services/{{ service_id }}"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
status_code: 200
|
||||
register: service
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
service_name: "{{ service.json.name }}"
|
||||
new_service_name: "{{ service.json.name }} {{ request_id }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
service_name: "command line invocation"
|
||||
new_service_name: "command line invocation"
|
||||
when: manageiq is not defined
|
||||
|
||||
- name: set service name
|
||||
uri:
|
||||
#url: "{{ endpoint }}/api/services/{{ service_id }}"
|
||||
url: "https://127.0.0.1/api/services/{{ service_id }}"
|
||||
validate_certs: no
|
||||
method: POST
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
body_format: json
|
||||
body: { "action" : "edit", "resource" : { "name" : "{{ new_service_name }}" }}
|
||||
status_code: 200, 204
|
||||
register: service
|
||||
when: manageiq is defined
|
||||
|
||||
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
name: Validation tasks
|
||||
vars:
|
||||
groupmemberslist: []
|
||||
groupmemberslistvalidate: []
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Build inventory for AD server
|
||||
add_host: >
|
||||
name=adserver
|
||||
groups=windows
|
||||
ansible_host="{{ ad_host }}"
|
||||
|
||||
- name: Fail Where Requisite Vars Not Set
|
||||
fail:
|
||||
msg: "Parameter {{item.key}} has value {{item.value}}, {{item.key}} is required to be passed from Cloudforms"
|
||||
when: item.value == 'placeholder'
|
||||
loop: "{{ lookup('dict', vars ) }}" #vars is a special variable of a list containing all variables in the playbook
|
||||
no_log: "{{ suppress_vars_output }}"
|
||||
|
||||
- name: Split groupmembers parameter on , delimiter
|
||||
set_fact:
|
||||
groupmemberslist: "{{ groupmemberslist }} + [ '{{ item }}' ]"
|
||||
with_items: "{{ groupmembers.split(',') }}"
|
||||
|
||||
- name: Remove empty fields from groupmembers parameter
|
||||
set_fact:
|
||||
groupmemberslistvalidate: "{{ groupmemberslistvalidate }} + [ '{{ item }}' ]"
|
||||
when: item | length != 0
|
||||
with_items: "{{ groupmemberslist }}"
|
||||
|
||||
- hosts: adserver
|
||||
gather_facts: false
|
||||
name: Check AD user exists
|
||||
vars:
|
||||
# set present/absent flag for netapp modules from create/delete values in the perform parameter, included here for action to perform
|
||||
state: "{{ 'present' if perform == 'create' else ( 'absent' if perform == 'delete' else 'placeholder') }}"
|
||||
groupmemberslistvalidate: "{{ hostvars['localhost']['groupmemberslistvalidate'] }}"
|
||||
email_customer: []
|
||||
no_aduser: []
|
||||
email_recipients: []
|
||||
user_no_action: []
|
||||
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
||||
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
||||
# NOT USED body_requester_user_ad: "{{ hostvars['localhost']['requester_user_ad }}"
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
# to avoid using group_vars we set_facts that were not accepted by add_host, add_host does not work with many windows winrm connectivity vars
|
||||
- name: Add connectivity variables for adserver
|
||||
set_fact:
|
||||
ansible_user: "{{ ad_user }}"
|
||||
ansible_password: "{{ ad_pass }}"
|
||||
ansible_connection: "{{ ad_connection }}"
|
||||
ansible_winrm_transport: "{{ ad_winrm_transport }}"
|
||||
ansible_winrm_kinit_mode: "{{ ad_winrm_kinit_mode }}"
|
||||
ansible_winrm_message_encryption: "{{ ad_winrm_message_encryption }}"
|
||||
ansible_port: "{{ ad_port }}"
|
||||
ansible_winrm_scheme: "{{ ad_winrm_scheme }}"
|
||||
ansible_winrm_server_cert_validation: "{{ ad_winrm_server_cert_validation }}"
|
||||
|
||||
- name: Check AD user exists
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne()
|
||||
register: command_result
|
||||
with_items: "{{ groupmemberslistvalidate }}"
|
||||
|
||||
- name: Flag fail where AD user not exist
|
||||
set_fact:
|
||||
no_aduser: "{{ no_aduser }} + [ '{{ item.item }}' ]"
|
||||
when: item.stdout | length == 0
|
||||
with_items: '{{ command_result.results }}'
|
||||
changed_when: true
|
||||
notify: topic_noad
|
||||
|
||||
- name: Get Domain user email address
|
||||
win_shell: Get-ADUser {{ item }} -Properties mail | Select-Object -ExpandProperty mail
|
||||
register: email_result
|
||||
with_items: "{{ groupmemberslistvalidate }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# this will crash out where customer account has no associated email - needs logic for empty elements
|
||||
- name: Add customer email to list of email recipients
|
||||
set_fact:
|
||||
email_customer: "{{ email_customer }} + [ '{{ item.stdout_lines[0] }}' ]"
|
||||
with_items: "{{ email_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Build list of user and accociated email address
|
||||
set_fact:
|
||||
account_email: "{{ account_email | default([]) + [dict(name=item[0], email=item[1])] }}"
|
||||
loop: "{{ groupmemberslistvalidate | zip (email_customer) | list }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Add Domain user/group to Domain Group
|
||||
win_domain_group_membership:
|
||||
name: "{{ group }}"
|
||||
members: "{{ groupmemberslistvalidate }}"
|
||||
state: "{{ state }}"
|
||||
register: result
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Build list of user emails requiring notification for add operation
|
||||
set_fact:
|
||||
email_recipients: "{{ email_recipients | default([]) + [item.email] }}"
|
||||
with_items: "{{ account_email }}"
|
||||
when: ( no_aduser | length == 0 and (result.added | length > 0 and state == 'present')) and item.name in result.added
|
||||
|
||||
- name: Build list of user emails requiring notification for delete operation
|
||||
set_fact:
|
||||
email_recipients: "{{ email_recipients | default([]) + [item.email] }}"
|
||||
with_items: "{{ account_email }}"
|
||||
when: ( no_aduser | length == 0 and (result.removed | length > 0 and state == 'absent')) and item.name in result.removed
|
||||
|
||||
# users in this list were not added/removed from AD group as they were already present/not-present
|
||||
- name: Build a list of users where no action was taken
|
||||
set_fact:
|
||||
user_no_action: "{{ user_no_action | default([]) + [item.name] }}"
|
||||
with_items: "{{ account_email }}"
|
||||
when: no_aduser | length == 0 and (item.name not in result.removed and item.name not in result.added)
|
||||
|
||||
- name: Build a list of emails for users where no action was taken
|
||||
set_fact:
|
||||
email_recipients_no_action: "{{ email_recipients_no_action | default([]) + [item.email] }}"
|
||||
with_items: "{{ account_email }}"
|
||||
when: no_aduser | length == 0 and (item.name not in result.removed and item.name not in result.added)
|
||||
|
||||
# - debug:
|
||||
# msg:
|
||||
# - "email recipients change {{ email_recipients }}"
|
||||
# - "email recipients nochange {{ email_recipients_no_action }}"
|
||||
# - "users not actioned {{ user_no_action }}"
|
||||
|
||||
- set_fact:
|
||||
template_prefix: "default"
|
||||
when: template_prefix is not defined
|
||||
|
||||
handlers:
|
||||
|
||||
- name: Build invalid user(s) email body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-invalid.j2') }}"
|
||||
delegate_to: localhost
|
||||
listen: topic_noad
|
||||
|
||||
- name: Send status email
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ hostvars['localhost']['requester_email'] }}"
|
||||
subject: "Result of request {{ body_service_name }} : {{ perform }} user(s) in {{ group }}"
|
||||
body: "{{ mail_body }}"
|
||||
delegate_to: localhost
|
||||
when: hostvars['localhost']['requester_email'] is defined and enable_requester_email == 'true'
|
||||
listen:
|
||||
- topic_noad
|
||||
|
||||
- name: Condition fail on invalid AD user
|
||||
debug:
|
||||
msg:
|
||||
- "invalid AD user(s) to {{ perform }} in group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "{{ no_aduser }}"
|
||||
- ---------------------------------------
|
||||
delegate_to: localhost
|
||||
listen: topic_noad
|
||||
|
||||
# provides failure exit code for cloudforms and terminates playbook
|
||||
- name: Hard exit
|
||||
fail:
|
||||
listen: topic_noad
|
||||
|
||||
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
name: Report Results
|
||||
vars:
|
||||
state: "{{ 'present' if perform == 'create' else ( 'absent' if perform == 'delete' else 'placeholder') }}"
|
||||
result: "{{ hostvars['adserver']['result'] }}"
|
||||
requester_email: "{{ hostvars['localhost']['requester_email'] }}"
|
||||
body_service_name_noreqid: "{{ hostvars['localhost']['service_name'] }}" # service_name matches cloudforms tile name (i.e Automated Transcription Service), we want this for the customer email subject line - welcome to <service_name>
|
||||
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
||||
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
||||
body_requester_user_ad: "{{ hostvars['localhost']['requester_user_ad'] }}"
|
||||
groupmemberslistvalidate: "{{ hostvars['localhost']['groupmemberslistvalidate'] }}"
|
||||
email_recipients: []
|
||||
email_recipients_no_action: []
|
||||
user_no_action: []
|
||||
self_service_user: false
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
# the initial design of the script catered for user(s) being added to a group and the requester getting status emails (add / remove / invalid-user / no-change) and the user(s) recieving (add / remove) emails
|
||||
# a use case where the requester is requesting only himself could mean status emails and user emails being recieved
|
||||
# logic at the end of this script evaluates if the requester user matches a single entry in groupmember list and then disables requester/status emails
|
||||
# to test this behaviour on the command line ensure the following fact (requester_user_ad) is set to the same value as the single item in the groupmembers parameter
|
||||
# this condition is replicated with the spoof_self_service parameter
|
||||
- name: checking for spoof self service mode
|
||||
set_fact:
|
||||
body_requester_user_ad: "{{ groupmemberslistvalidate[0] }}"
|
||||
when: spoof_self_service == "true"
|
||||
|
||||
- name: detect if the requester user is adding/removing only itself to group (only active when using a custom template_prefix)
|
||||
set_fact:
|
||||
self_service_user: true
|
||||
when: (groupmemberslistvalidate | length == 1 and groupmemberslistvalidate[0] == body_requester_user_ad) and template_prefix != 'default'
|
||||
|
||||
- debug:
|
||||
msg: "running in self service mode"
|
||||
when: self_service_user
|
||||
|
||||
- name: set list of email recipients where action has been taken upon their account
|
||||
set_fact:
|
||||
email_recipients: "{{ hostvars['adserver']['email_recipients'] }}"
|
||||
when: hostvars['adserver']['email_recipients'] is defined
|
||||
|
||||
- name: set list of email recipients where no action has been taken upon their account
|
||||
set_fact:
|
||||
email_recipients_no_action: "{{ hostvars['adserver']['email_recipients_no_action'] }}"
|
||||
when: hostvars['adserver']['email_recipients_no_action'] is defined
|
||||
|
||||
- name: set list of user names where no action has been taken upon their account
|
||||
set_fact:
|
||||
user_no_action: "{{ hostvars['adserver']['user_no_action'] }}"
|
||||
when: hostvars['adserver']['user_no_action'] is defined
|
||||
|
||||
- name: Print email recipient information
|
||||
debug:
|
||||
msg:
|
||||
- "requester to recieve status email: {{ requester_email }}"
|
||||
- "enable requester mail: {{ enable_requester_email }}"
|
||||
- "enable customer mail: {{ enable_customer_email }}"
|
||||
- "self service mode: {{ self_service_user }}"
|
||||
- "user(s) that can recieve change notification: {{ email_recipients }}"
|
||||
- "user(s) with no action undertaken: {{ user_no_action }}"
|
||||
- "user(s) that can recieve nochange notification: {{ email_recipients_no_action }}"
|
||||
|
||||
- set_fact:
|
||||
template_prefix: "default"
|
||||
when: template_prefix is not defined
|
||||
|
||||
- name: Build nochange email status body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-nochange.j2') }}"
|
||||
when: not result.changed
|
||||
|
||||
- debug: # changed_when: false in 'build nochange email' doesnt force notification of topic, use debug as a work around to always get a changed: true status to ensure a desired handler is executed
|
||||
msg: "Invoke nochange handler"
|
||||
changed_when: true
|
||||
when: not result.changed
|
||||
notify: topic_nochange
|
||||
|
||||
- name: Build add email status body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-add.j2') }}"
|
||||
changed_when: true
|
||||
when: result.changed and result.added |length > 0
|
||||
notify: topic_add
|
||||
|
||||
- name: Build remove email status body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-remove.j2') }}"
|
||||
changed_when: true
|
||||
when: result.changed and result.removed |length > 0
|
||||
notify: topic_remove
|
||||
|
||||
- name: Build add email customer body
|
||||
set_fact:
|
||||
customer_change_mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/customer-mail-add.j2') }}"
|
||||
changed_when: true
|
||||
when: (result.changed and result.added |length > 0) and template_prefix != 'default'
|
||||
notify: topic_add
|
||||
|
||||
- name: Build remove email customer body
|
||||
set_fact:
|
||||
customer_change_mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/customer-mail-remove.j2') }}"
|
||||
changed_when: true
|
||||
when: (result.changed and result.removed |length > 0) and template_prefix != 'default'
|
||||
notify: topic_remove
|
||||
|
||||
- name: Build nochange add email customer body
|
||||
set_fact:
|
||||
customer_nochange_add_mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/customer-mail-nochange.j2') }}"
|
||||
changed_when: true
|
||||
when: user_no_action |length >0 and template_prefix != 'default'
|
||||
notify: topic_nochange
|
||||
|
||||
handlers:
|
||||
|
||||
- name: Send status email
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ requester_email }}"
|
||||
subject: "Result of request {{ body_service_name }} : {{ perform }} user(s) in {{ group }}"
|
||||
body: "{{ mail_body }}"
|
||||
#body: "{{ lookup('file', '/var/www/release.txt') }}" # not using files due to WSL/ANSIBLE_RUNNER virtual environments
|
||||
#when: hostvars['localhost']['requester_email'] is defined and enable_requester_email == 'true'
|
||||
when: (hostvars['localhost']['requester_email'] is defined and enable_requester_email == 'true') and not self_service_user
|
||||
listen:
|
||||
- topic_nochange
|
||||
- topic_add
|
||||
- topic_remove
|
||||
|
||||
- name: Send customer add / remove email
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ email_recipients }}"
|
||||
#subject: "Cloudforms - Result of {{ body_service_name }}"
|
||||
subject: "Welcome to the {{ body_service_name_noreqid }}"
|
||||
# no attachments, cloudforms ansible-runner has trouble using directory paths
|
||||
# we cant use file filter with binary files such as images
|
||||
# instead our html body should reference the logo @ https://www.nottingham.ac.uk/SiteElements/Images/Base/logo.png
|
||||
#attach: 'templates/{{ template_prefix }}/images/logo.png'
|
||||
subtype: html
|
||||
body: "{{ customer_change_mail_body }}"
|
||||
when: (enable_customer_email == 'true' and email_recipients | length >0) and template_prefix != 'default'
|
||||
listen:
|
||||
- topic_add
|
||||
- topic_remove
|
||||
|
||||
- name: Send customer nochange add email
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ email_recipients_no_action }}"
|
||||
subject: "Welcome to the {{ body_service_name_noreqid }}"
|
||||
subtype: html
|
||||
body: "{{ customer_nochange_add_mail_body }}"
|
||||
#when: (enable_customer_email == 'true' and email_recipients_no_action | length >0) and template_prefix != 'default'
|
||||
when: ((enable_customer_email == 'true' and email_recipients_no_action | length >0) and template_prefix != 'default') and state == 'present'
|
||||
listen:
|
||||
- topic_nochange
|
||||
|
||||
# commit message "disable customer email for scenario: no change with delete action"
|
||||
# quick change so the customer nochange email (no longer being rendered with create/delete value of the perform variable) is only sent if a user is being added and is already in a group
|
||||
# there is no nochange email for a user who is being removed from a group who is already not a member
|
||||
# we likely dont want to fail on this condition when doing lookup on AD as other users still want to be processed (we only fail on invalid users for safety)
|
||||
# is a customer nochange remove email useful? probably not
|
||||
# Clarify with Mike
|
||||
|
||||
# - name: Send email
|
||||
# debug:
|
||||
# msg: "{{ mail_body }}"
|
||||
# listen:
|
||||
# - topic_nochange
|
||||
# - topic_add
|
||||
# - topic_remove
|
||||
|
||||
- name: Condition no change
|
||||
debug:
|
||||
msg:
|
||||
- "no changes made to group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "users that had no action taken due to already being {{ state }} in group"
|
||||
- ---------------------------------------
|
||||
- "{{ user_no_action }}"
|
||||
- ---------------------------------------
|
||||
- "members of group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "{{ result.members }}"
|
||||
- ---------------------------------------
|
||||
listen: topic_nochange
|
||||
|
||||
- name: Condition user(s) added
|
||||
debug:
|
||||
msg:
|
||||
- "user(s) added to group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "{{ result.added }}"
|
||||
- ---------------------------------------
|
||||
- "users that had no action taken due to already being {{ state }} in group"
|
||||
- ---------------------------------------
|
||||
- "{{ user_no_action }}"
|
||||
- ---------------------------------------
|
||||
- "members of group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "{{ result.members }}"
|
||||
- ---------------------------------------
|
||||
listen: topic_add
|
||||
|
||||
- name: Condition user(s) removed
|
||||
debug:
|
||||
msg:
|
||||
- "user(s) removed from group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "{{ result.removed }}"
|
||||
- ---------------------------------------
|
||||
- "users that had no action taken due to already being {{ state }} in group"
|
||||
- ---------------------------------------
|
||||
- "{{ user_no_action }}"
|
||||
- ---------------------------------------
|
||||
- "members of group {{ group }}"
|
||||
- ---------------------------------------
|
||||
- "{{ result.members }}"
|
||||
- ---------------------------------------
|
||||
listen: topic_remove
|
||||
|
||||
# may need to test verbosity levels with handlers for clean log output in cloudforms, example of using verbosity level
|
||||
#
|
||||
# - name: Print Results
|
||||
# debug:
|
||||
# var: result
|
||||
# verbosity: 0
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
user(s) added to group {{ group }};
|
||||
|
||||
{% for entry in result.added %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
users that had no action taken due to already being {{ state }};
|
||||
|
||||
{% for entry in user_no_action %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
members of group {{ group }}, current members include;
|
||||
|
||||
{% for entry in result.members %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
invalid AD user(s) {{ perform }} in group {{ group }};
|
||||
|
||||
{% for entry in no_aduser %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
job failed.
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
no changes made to group {{ group }} with {{ perform }} operation
|
||||
|
||||
users that had no action taken due to already being {{ state }};
|
||||
|
||||
{% for entry in user_no_action %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
members of group {{ group }}, current members include;
|
||||
|
||||
{% for entry in result.members %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
user(s) removed from group {{ group }};
|
||||
|
||||
{% for entry in result.removed %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
users that had no action taken due to already being {{ state }};
|
||||
|
||||
{% for entry in user_no_action %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
members of group {{ group }}, current members include;
|
||||
|
||||
{% for entry in result.members %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
|
||||
<head>
|
||||
<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||
<meta content="width=device-width" name="viewport"/>
|
||||
<!--[if !mso]><!-->
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
|
||||
<!--<![endif]-->
|
||||
<title></title>
|
||||
<!--[if !mso]><!-->
|
||||
<!--<![endif]-->
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table,
|
||||
td,
|
||||
tr {
|
||||
vertical-align: top;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
* {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
a[x-apple-data-detectors=true] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
<style id="media-query" type="text/css">
|
||||
@media (max-width: 520px) {
|
||||
|
||||
.block-grid,
|
||||
.col {
|
||||
min-width: 320px !important;
|
||||
max-width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.block-grid {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col>div {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
img.fullwidth,
|
||||
img.fullwidthOnMobile {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.no-stack .col {
|
||||
min-width: 0 !important;
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
||||
.no-stack.two-up .col {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num8 {
|
||||
width: 66% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num3 {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num6 {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num9 {
|
||||
width: 75% !important;
|
||||
}
|
||||
|
||||
.video-block {
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.mobile_hide {
|
||||
min-height: 0px;
|
||||
max-height: 0px;
|
||||
max-width: 0px;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
font-size: 0px;
|
||||
}
|
||||
|
||||
.desktop_hide {
|
||||
display: block !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;">
|
||||
<!--[if IE]><div class="ie-browser"><![endif]-->
|
||||
<table bgcolor="#FFFFFF" cellpadding="0" cellspacing="0" class="nl-container" role="presentation" style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #FFFFFF; width: 100%;" valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td style="word-break: break-word; vertical-align: top;" valign="top">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:#FFFFFF"><![endif]-->
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid" style="Margin: 0 auto; min-width: 320px; max-width: 500px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: transparent;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:500px"><tr class="layout-full-width" style="background-color:transparent"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="500" style="background-color:transparent;width:500px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12" style="min-width: 320px; max-width: 500px; display: table-cell; vertical-align: top; width: 500px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<div align="left" class="img-container left autowidth" style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img alt="Alternate text" border="0" class="left autowidth" src="https://www.nottingham.ac.uk/SiteElements/Images/Base/logo.png" style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 243px; display: block;" title="Alternate text" width="243"/>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 24px;">
|
||||
<p style="font-size: 18px; line-height: 2; word-break: break-word; mso-line-height-alt: 36px; margin: 0;"><span style="font-size: 18px;">Welcome to the Automated Transcription Service</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">You have been given permission to use the Automated Transcription Service.</span></p>
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">Please login here with your University username and password:</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 24px;">
|
||||
<p style="font-size: 14px; line-height: 2; word-break: break-word; mso-line-height-alt: 28px; margin: 0;"><a href="https://autotranscription.nottingham.ac.uk" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">https://autotranscription.nottingham.ac.uk</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;">Please remember:</p>
|
||||
<ul>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">Usage is charged per hour of audio and one month in arrears to a University project code</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">Audio files deleted immediately after transcription and transcripts after 90 days</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">To comply with General Data Protection Regulation (GDPR) you must tell your participants (e.g. in a Participant Information sheet) that the University of Nottingham’s Automated Transcription Service is being used to process audio of their speech</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">In many cases the service can reach accuracy levels of 70-99%, but factors such as background noise and recording quality have a significant impact</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">The service can only transcribe English-language audio</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;">If you are unsure about whether the Automated Transcription Service is the correct tool for your research, please contact a Digital Research Specialist.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;"><a href="https://uniofnottm.sharepoint.com/sites/DigitalResearch/SitePages/Automated-Transcription.aspx" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">More information about the service, including video guides, comprehensive FAQs and the service usage policy</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">Best wishes,</span><br/><span style="font-size: 14px;">Digital Research</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (IE)]></div><![endif]-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
|
||||
<head>
|
||||
<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||
<meta content="width=device-width" name="viewport"/>
|
||||
<!--[if !mso]><!-->
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
|
||||
<!--<![endif]-->
|
||||
<title></title>
|
||||
<!--[if !mso]><!-->
|
||||
<!--<![endif]-->
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table,
|
||||
td,
|
||||
tr {
|
||||
vertical-align: top;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
* {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
a[x-apple-data-detectors=true] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
<style id="media-query" type="text/css">
|
||||
@media (max-width: 520px) {
|
||||
|
||||
.block-grid,
|
||||
.col {
|
||||
min-width: 320px !important;
|
||||
max-width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.block-grid {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col>div {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
img.fullwidth,
|
||||
img.fullwidthOnMobile {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.no-stack .col {
|
||||
min-width: 0 !important;
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
||||
.no-stack.two-up .col {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num8 {
|
||||
width: 66% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num3 {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num6 {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num9 {
|
||||
width: 75% !important;
|
||||
}
|
||||
|
||||
.video-block {
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.mobile_hide {
|
||||
min-height: 0px;
|
||||
max-height: 0px;
|
||||
max-width: 0px;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
font-size: 0px;
|
||||
}
|
||||
|
||||
.desktop_hide {
|
||||
display: block !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;">
|
||||
<!--[if IE]><div class="ie-browser"><![endif]-->
|
||||
<table bgcolor="#FFFFFF" cellpadding="0" cellspacing="0" class="nl-container" role="presentation" style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #FFFFFF; width: 100%;" valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td style="word-break: break-word; vertical-align: top;" valign="top">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:#FFFFFF"><![endif]-->
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid" style="Margin: 0 auto; min-width: 320px; max-width: 500px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: transparent;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:500px"><tr class="layout-full-width" style="background-color:transparent"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="500" style="background-color:transparent;width:500px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12" style="min-width: 320px; max-width: 500px; display: table-cell; vertical-align: top; width: 500px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<div align="left" class="img-container left autowidth" style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img alt="Alternate text" border="0" class="left autowidth" src="https://www.nottingham.ac.uk/SiteElements/Images/Base/logo.png" style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 243px; display: block;" title="Alternate text" width="243"/>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 24px;">
|
||||
<p style="font-size: 18px; line-height: 2; word-break: break-word; mso-line-height-alt: 36px; margin: 0;"><span style="font-size: 18px;">Welcome to the Automated Transcription Service</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">You already have permission to use the Automated Transcription Service.</span></p>
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">Please login here with your University username and password:</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 24px;">
|
||||
<p style="font-size: 14px; line-height: 2; word-break: break-word; mso-line-height-alt: 28px; margin: 0;"><a href="https://autotranscription.nottingham.ac.uk" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">https://autotranscription.nottingham.ac.uk</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;">Please remember:</p>
|
||||
<ul>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">Usage is charged per hour of audio and one month in arrears to a University project code</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">Audio files deleted immediately after transcription and transcripts after 90 days</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">To comply with General Data Protection Regulation (GDPR) you must tell your participants (e.g. in a Participant Information sheet) that the University of Nottingham’s Automated Transcription Service is being used to process audio of their speech</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">In many cases the service can reach accuracy levels of 70-99%, but factors such as background noise and recording quality have a significant impact</li>
|
||||
<li style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px;">The service can only transcribe English-language audio</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;">If you are unsure about whether the Automated Transcription Service is the correct tool for your research, please contact a Digital Research Specialist.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;"><a href="https://uniofnottm.sharepoint.com/sites/DigitalResearch/SitePages/Automated-Transcription.aspx" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">More information about the service, including video guides, comprehensive FAQs and the service usage policy</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">Best wishes,</span><br/><span style="font-size: 14px;">Digital Research</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (IE)]></div><![endif]-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
|
||||
<head>
|
||||
<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||
<meta content="width=device-width" name="viewport"/>
|
||||
<!--[if !mso]><!-->
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
|
||||
<!--<![endif]-->
|
||||
<title></title>
|
||||
<!--[if !mso]><!-->
|
||||
<!--<![endif]-->
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table,
|
||||
td,
|
||||
tr {
|
||||
vertical-align: top;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
* {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
a[x-apple-data-detectors=true] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
<style id="media-query" type="text/css">
|
||||
@media (max-width: 520px) {
|
||||
|
||||
.block-grid,
|
||||
.col {
|
||||
min-width: 320px !important;
|
||||
max-width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.block-grid {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col>div {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
img.fullwidth,
|
||||
img.fullwidthOnMobile {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.no-stack .col {
|
||||
min-width: 0 !important;
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
||||
.no-stack.two-up .col {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num8 {
|
||||
width: 66% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num3 {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num6 {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num9 {
|
||||
width: 75% !important;
|
||||
}
|
||||
|
||||
.video-block {
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.mobile_hide {
|
||||
min-height: 0px;
|
||||
max-height: 0px;
|
||||
max-width: 0px;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
font-size: 0px;
|
||||
}
|
||||
|
||||
.desktop_hide {
|
||||
display: block !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;">
|
||||
<!--[if IE]><div class="ie-browser"><![endif]-->
|
||||
<table bgcolor="#FFFFFF" cellpadding="0" cellspacing="0" class="nl-container" role="presentation" style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #FFFFFF; width: 100%;" valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td style="word-break: break-word; vertical-align: top;" valign="top">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:#FFFFFF"><![endif]-->
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid" style="Margin: 0 auto; min-width: 320px; max-width: 500px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: transparent;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:500px"><tr class="layout-full-width" style="background-color:transparent"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="500" style="background-color:transparent;width:500px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12" style="min-width: 320px; max-width: 500px; display: table-cell; vertical-align: top; width: 500px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<div align="left" class="img-container left autowidth" style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img alt="Alternate text" border="0" class="left autowidth" src="https://www.nottingham.ac.uk/SiteElements/Images/Base/logo.png" style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 243px; display: block;" title="Alternate text" width="243"/>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">You have been removed from the Automated Transcription Service.</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="font-size: 14px; line-height: 1.2; word-break: break-word; mso-line-height-alt: 17px; margin: 0;"><a href="https://uniofnottm.sharepoint.com/sites/DigitalResearch/SitePages/Automated-Transcription.aspx" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">More information about the service</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:Arial, Helvetica Neue, Helvetica, sans-serif;line-height:1.2;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="line-height: 1.2; font-size: 12px; color: #555555; font-family: Arial, Helvetica Neue, Helvetica, sans-serif; mso-line-height-alt: 14px;">
|
||||
<p style="line-height: 1.2; word-break: break-word; font-size: 14px; mso-line-height-alt: 17px; margin: 0;"><span style="font-size: 14px;">Best wishes,</span><br/><span style="font-size: 14px;">Digital Research</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (IE)]></div><![endif]-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
user(s) added to group {{ group }};
|
||||
|
||||
{% for entry in result.added %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
users that had no action taken due to already being {{ state }};
|
||||
|
||||
{% for entry in user_no_action %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
invalid AD user(s) {{ perform }} in group {{ group }};
|
||||
|
||||
{% for entry in no_aduser %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
job failed.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
no changes made to group {{ group }} with {{ perform }} operation
|
||||
|
||||
users that had no action taken due to already being {{ state }};
|
||||
|
||||
{% for entry in user_no_action %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
user(s) removed from group {{ group }};
|
||||
|
||||
{% for entry in result.removed %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
users that had no action taken due to already being {{ state }};
|
||||
|
||||
{% for entry in user_no_action %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
# Run on command line with -e for override variables or from Cloudforms with parameters, use comma delimiter when specifing multiple users:
|
||||
#
|
||||
# command line run with no email (not via cloudforms)
|
||||
#
|
||||
# ansible-playbook adgroup.yml -e 'groupmembers="tseed,swright" \
|
||||
# group=Project \
|
||||
# perform=delete \
|
||||
# ad_host=WIN-1JE0R5GCBSG.NETAPPSIM.LOCAL \
|
||||
# ad_user="administrator@NETAPPSIM.LOCAL" \
|
||||
# ad_pass="Password0" \
|
||||
# from_email=tseed@ocf.co.uk \
|
||||
# enable_requester_email=false \
|
||||
# enable_customer_email=false \
|
||||
# api_user=dummy \
|
||||
# api_pass=dummy'
|
||||
#
|
||||
# command line run with email and non default customer email templates (not via cloudforms)
|
||||
#
|
||||
# ansible-playbook adgroup.yml -e 'groupmembers="tseed,swright" \
|
||||
# group=Project \
|
||||
# perform=delete \
|
||||
# ad_host=WIN-1JE0R5GCBSG.NETAPPSIM.LOCAL \
|
||||
# ad_user=administrator \
|
||||
# ad_pass="Password0" \
|
||||
# from_email="noreply@cloudforms" \
|
||||
# enable_requester_email=true \
|
||||
# enable_customer_email=true \
|
||||
# smtp_relay=192.168.101.240 \
|
||||
# smtp_port=25 \
|
||||
# template_prefix=transcription \
|
||||
# requester_email=tseed@ocf.co.uk \
|
||||
# api_user=dummy \
|
||||
# api_pass=dummy'
|
||||
#
|
||||
# command line run with email, non default customer email templates, a single groupmember and spoof self service mode enabled (replicate cloudforms provisioning for the requester)
|
||||
# this will disable the enable_requester_email
|
||||
#
|
||||
# ansible-playbook adgroup.yml -e 'groupmembers="tseed" \
|
||||
# group=Project \
|
||||
# perform=delete \
|
||||
# ad_host=WIN-1JE0R5GCBSG.NETAPPSIM.LOCAL \
|
||||
# ad_user=administrator \
|
||||
# ad_pass="Password0" \
|
||||
# from_email="noreply@cloudforms" \
|
||||
# enable_requester_email=true \
|
||||
# enable_customer_email=true \
|
||||
# smtp_relay=192.168.101.240 \
|
||||
# smtp_port=25 \
|
||||
# template_prefix=transcription \
|
||||
# requester_email=tseed@ocf.co.uk \
|
||||
# api_user=dummy \
|
||||
# api_pass=dummy \
|
||||
# spoof_self_service=true'
|
||||
#
|
||||
# requester email templates are prefixed mail-, customer email templates customer-, customer emails are in html format
|
||||
# customers will only recieve an add/remove email when this action has been performed upon their account
|
||||
# when template_prefix is omitted the value is set to default and default email templates used, in this scenario customer emails are not sent disregarding the parameter enable_customer_email
|
||||
#
|
||||
# variables are evaluated for the term 'placeholder', when found the playbook will exit
|
||||
# variables when evaluated for the term 'placeholder' have their output supressed to ensure the cloudforms log doesnt include sensitive parameters such as passwords
|
||||
# to assist with debug pass an optional paramter of 'suppress_vars_output=false'
|
||||
#
|
||||
---
|
||||
# variables to create in-memory inventory of the AD server, notice these variables are in the winrm format that would be under the entry [<hostgroup>:vars] for an inventory file
|
||||
# these variables can also be passed with as parameters by cloudforms and will override any instances in this file ( cloudforms uses ansible --extra-vars)
|
||||
# any variables set as 'placeholder' must be passed as parameters at runtime else exit
|
||||
#
|
||||
ad_host: placeholder # active directory server, when using kerberos (with requisite resolv.conf entry) this must be a fqdn
|
||||
ad_user: placeholder # AD service account capable of manipulating group membership
|
||||
ad_pass: "placeholder" # AD service account password
|
||||
ad_connection: winrm
|
||||
ad_winrm_transport: kerberos
|
||||
ad_winrm_kinit_mode: managed # allow ansible to manage own kerberos token, SSSD manages when set to manual
|
||||
ad_winrm_message_encryption: auto # can be set to always, depends on ad server profile
|
||||
ad_port: 5986 # 5985/http for non https transport, UON on-prem use 5986/https
|
||||
ad_winrm_scheme: https # UON on-prem use 5986/https
|
||||
ad_winrm_server_cert_validation: ignore
|
||||
|
||||
# set to false for task "Fail Where Requisite Vars Not Set" to output all variables to assist debug
|
||||
suppress_vars_output: true
|
||||
|
||||
# control email recipients
|
||||
enable_requester_email: false
|
||||
enable_customer_email: false
|
||||
|
||||
# control self service mode, this is to replicate cloudforms bahaviour where the requester populates the groupmembers parameter only with itself
|
||||
# this mode effectively disables status emails even if enable_requester_email: true is passed as a parameter
|
||||
spoof_self_service: false
|
||||
|
||||
# smtp server
|
||||
smtp_relay: smtp.nottingham.ac.uk
|
||||
smtp_port: 25
|
||||
|
||||
# email template path, this value changes the template path to ./templates/<value>/ , the value is set to default when not specified as a parameter in cloudforms
|
||||
# default templates list all usable variables that maybe used in a template
|
||||
# when changing the prefix, ensure a matching folder name exists in the templates folder containing file; mail-add.j2, mail-invalid.j2, mail-nochange.j2, mail-remove.j2
|
||||
template_prefix: "default"
|
||||
|
||||
# mandatory parameters for actions and targets
|
||||
perform: placeholder # should be "create" or "delete"
|
||||
groupmembers: placeholder # users or groups to be added into group, this is a comma separated list, single entries are converted to a list with a single item
|
||||
group: placeholder # group, single group accepted
|
||||
from_email: placeholder # should be a service account address such as donotreply@nottingham.ac.uk
|
||||
|
||||
# Cloudforms API
|
||||
api_user: placeholder
|
||||
api_pass: placeholder
|
||||
|
||||
# groups for use in UON tiles
|
||||
# UI-Transcription-Live
|
||||
# UI-High-Performance-Windows
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
## What is this playbook?
|
||||
|
||||
|
||||
|
||||
It provisions GPFS storage shared over samba, it is designed to be run parameterised from cloudforms.
|
||||
|
||||
|
||||
|
||||
The playbook was tested on a development build of a GPFS single node cluster with CES service, the associated ARM template provisions an Azure instance with compatible IP scheme: Ansible Playbooks/ARM_templates/dev/rev1/spectrum_scale_ARM_v2.json
|
||||
|
||||
|
||||
|
||||
This playbook is functional but unfinished requiring input validation for cloudforms parameterise, better error condition reporting and customised UoN branded HTML customer notification email templates.
|
||||
|
||||
|
||||
|
||||
Time was called when the production GPFS CES host was not joined to the Active Directory domain in a way that would return windows SID and enable domain user logon to the resultant samba share, RCF2307 is planned to be implemented.
|
||||
|
||||
|
||||
|
||||
## The playbook demonstrates
|
||||
|
||||
|
||||
|
||||
Checks cloudforms environment and changes the name of the service to include the request ID to uniquely identify what has been ordered.
|
||||
|
||||
|
||||
|
||||
Check the AD users/groups provided for the share permissions are valid.
|
||||
|
||||
|
||||
|
||||
Builds a list of users/groups who will have access to the samba share.
|
||||
|
||||
|
||||
|
||||
Builds a list of users is nested groups and looks up from AD their associated email address to be used in access notification emails.
|
||||
|
||||
|
||||
|
||||
Creates a GPFS fileset via API.
|
||||
|
||||
|
||||
|
||||
Creates a GPFS fileset quota via API.
|
||||
|
||||
|
||||
|
||||
Creates a GPFS samba share via API.
|
||||
|
||||
|
||||
|
||||
Creates a GPFS samba share ACL via API.
|
||||
|
||||
|
||||
|
||||
Reports results via console and email.
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
---
|
||||
- name: multi loop
|
||||
block:
|
||||
- name: Reset loop variables # without unsetting infinte loops will occur
|
||||
set_fact:
|
||||
group_present: false
|
||||
find_groups: []
|
||||
#find_users: [] # we dont want to reset this var, this will append on each loop until exit (unexpected behaviour but works)
|
||||
find_group_members_tidy: []
|
||||
group_members: []
|
||||
member_type: []
|
||||
member_attributes: []
|
||||
|
||||
# - name: Inspect dummy host object_attributes
|
||||
# debug:
|
||||
# msg: "{{ hostvars['DUMMY_HOST']['object_attributes'] }}"
|
||||
|
||||
- name: Filter groups into a list - CHANGED
|
||||
set_fact:
|
||||
#find_groups: "{{ find_groups | default([]) }} + ['{{ item.name }}']"
|
||||
find_groups: "{{ find_groups | default([]) + [dict(name=item.name, role=item.role, type=item.type)] }}"
|
||||
with_items: "{{ hostvars['DUMMY_HOST']['object_attributes'] }}"
|
||||
when: item.type == 'Group'
|
||||
|
||||
- name: Append users to list - CHANGED
|
||||
set_fact:
|
||||
#find_users: "{{ find_users | default([]) }} + ['{{ item.name }}']"
|
||||
find_users: "{{ find_users | default([]) + [dict(name=item.name, role=item.role, type=item.type)] }}"
|
||||
with_items: "{{ hostvars['DUMMY_HOST']['object_attributes'] }}"
|
||||
when: item.type == 'Person'
|
||||
|
||||
# - debug:
|
||||
# msg:
|
||||
# - "{{ find_groups }}"
|
||||
# - "{{ find_users }}"
|
||||
# when: find_groups is defined
|
||||
|
||||
####### working
|
||||
# - name: Query group members - CHANGED
|
||||
# #win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne().Properties.member
|
||||
# win_shell: ([ADSISearcher] "(sAMAccountName={{ item.name }})").FindOne().Properties.member
|
||||
# register: find_group_members_result
|
||||
# with_items: "{{ find_groups }}"
|
||||
# #when: find_groups is defined
|
||||
# when: find_groups is defined and item.role == 'member'
|
||||
|
||||
# - name: Query group members type owner - NEW
|
||||
# #win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne().Properties.member
|
||||
# win_shell: ([ADSISearcher] "(sAMAccountName={{ item.name }})").FindOne().Properties.member
|
||||
# register: find_group_members_result_type_owner
|
||||
# with_items: "{{ find_groups }}"
|
||||
# when: find_groups is defined and item.role == 'owner' # instead of owner/member this could be RW/RWX
|
||||
####### working
|
||||
|
||||
- name: Query group members
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item.name }})").FindOne().Properties.member
|
||||
register: find_group_members_result
|
||||
with_items: "{{ find_groups }}"
|
||||
when: find_groups is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ find_group_members_result }}"
|
||||
# when: find_group_members_result is defined
|
||||
|
||||
####### working
|
||||
# - name: Tidy group members into list - CHANGED - stuck here
|
||||
# set_fact:
|
||||
# #group_members: "{{ group_members | default([]) + [item.split(',')[0].split('CN=')[1]] }}"
|
||||
# group_members: "{{ group_members | default([]) + [dict(name=item.split(',')[0].split('CN=')[1], role='member')] }}"
|
||||
# with_items: "{{ find_group_members_result | json_query(jmesquery) }}"
|
||||
# vars:
|
||||
# jmesquery: "results[].stdout_lines"
|
||||
# #when: find_group_members_result.results[0].stdout | length >0 # bad check
|
||||
# when: find_group_members_result is defined and (item is defined and item | length >0)
|
||||
|
||||
# - name: Tidy group members type owner into list - NEW
|
||||
# set_fact:
|
||||
# group_members: "{{ group_members | default([]) + [dict(name=item.split(',')[0].split('CN=')[1], role='owner')] }}"
|
||||
# with_items: "{{ find_group_members_result_type_owner | json_query(jmesquery) }}"
|
||||
# vars:
|
||||
# jmesquery: "results[].stdout_lines"
|
||||
# when: find_group_members_result_type_owner is defined and (item is defined and item | length >0)
|
||||
####### working
|
||||
|
||||
- name: Tidy group members into dict of names and owner
|
||||
set_fact:
|
||||
find_group_members_tidy: '{{ find_group_members_tidy | default([]) + [dict(name=item.name, role=item.role)] }}'
|
||||
with_items: "{{ find_group_members_result | json_query(jmesquery) }}"
|
||||
vars:
|
||||
jmesquery: "results[].{role: @.item.role, name: @.stdout_lines}"
|
||||
#entry: "{{ item.role }}"
|
||||
|
||||
- name: Tidy group members into dict of name and owner
|
||||
set_fact:
|
||||
group_members: "{{ group_members | default([]) + [dict(name=item.1.split(',')[0].split('CN=')[1], role=item.0.role)] }}"
|
||||
with_subelements:
|
||||
- "{{ find_group_members_tidy }}"
|
||||
- name
|
||||
|
||||
# - name: Tidy group members type owner into list - NEW (sort into dictionary with role as key)
|
||||
# set_fact:
|
||||
# group_members: '{{ group_members | default([]) + [dict(entry)] }}' # works cool
|
||||
# with_items: "{{ find_group_members_result | json_query(jmesqueryC) }}"
|
||||
# vars:
|
||||
# jmesqueryC: "results[].{role: @.item.role, name: @.stdout_lines}"
|
||||
# entry: '{"{{ item.role }}":"{{ item.name }}"}'
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ group_members }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Check AD object is user or group - CHANGED
|
||||
#win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne().Properties.objectcategory
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item.name }})").FindOne().Properties.objectcategory
|
||||
register: member_type_result
|
||||
with_items: "{{ group_members }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ member_type_result }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Build list of AD object type
|
||||
set_fact:
|
||||
member_type: "{{ member_type | default([]) + [(item.stdout.split(',')[0].split('CN=')[1])] }}"
|
||||
with_items: "{{ member_type_result.results }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ member_type }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Build dict of object names and types - CHANGED
|
||||
set_fact:
|
||||
#member_attributes: "{{ member_attributes | default([]) + [ dict(name=item[0], type=item[1]) ] }}"
|
||||
member_attributes: "{{ member_attributes | default([]) + [ dict(name=item[0].name, role=item[0].role, type=item[1]) ] }}" # effectively adding a new positional field from the list to the dict
|
||||
loop: "{{ group_members|zip(member_type)|list }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ member_attributes }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Reset/Add variable object_attributes to dummy host for next loop
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
object_attributes: "{{ member_attributes }}"
|
||||
when: member_attributes is defined
|
||||
|
||||
- name: Set flag to notify there are nested groups
|
||||
set_fact:
|
||||
group_present: true
|
||||
with_items: "{{ member_attributes }}"
|
||||
when: member_attributes is defined and item.type == 'Group'
|
||||
#when: member_attributes is defined and item.type == 'GroupA' # used to break loop with test for no group_present
|
||||
|
||||
- name: Nested group present?
|
||||
fail:
|
||||
msg: Nested group detected
|
||||
when: group_present
|
||||
|
||||
# the following tasks only run on the last run of the loop where there are no more groups detected
|
||||
# append the users from the last run of the loop to find_users
|
||||
# set find_users as a DUMMY_HOST variable to be retrieved from the calling script - this is how you pass variables back from different plays also works for include_tasks
|
||||
- name: Append users to list - CHANGED
|
||||
set_fact:
|
||||
#find_users: "{{ find_users | default([]) + [dict(name=item.name, type=member)] }}"
|
||||
find_users: "{{ find_users | default([]) + [dict(name=item.name, type=item.type, role=item.role)] }}"
|
||||
with_items: "{{ member_attributes }}"
|
||||
when: item.type == 'Person'
|
||||
|
||||
- name: Add users to variable find_users for dummy host on final loop
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
find_users: "{{ find_users }}"
|
||||
when: find_users is defined
|
||||
|
||||
rescue:
|
||||
|
||||
- include_tasks: group_lookup.yml
|
||||
|
|
@ -0,0 +1,589 @@
|
|||
---
|
||||
# PLAY
|
||||
# Gather cloudforms information and set service name
|
||||
- name: Query automate workspace
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
|
||||
tasks:
|
||||
|
||||
- set_fact:
|
||||
endpoint: "{{ manageiq.api_url }}"
|
||||
auth_token: "{{ manageiq.api_token }}"
|
||||
request_id: "{{ (manageiq.request).split('/')[-1] }}"
|
||||
service_id: "{{ (manageiq.service).split('/')[-1] }}"
|
||||
user_id: "{{ (manageiq.user).split('/')[-1] }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# when users run ansible their manageiq auth token does not have sufficient rights to interact with the API, no combination of rights in a role for a non admin user are sufficient
|
||||
# use the local admin credentials to get an auth token
|
||||
- name: Get auth token
|
||||
uri:
|
||||
url: "{{ endpoint }}/api/auth"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
user: "{{ api_user }}"
|
||||
password: "{{ api_pass }}"
|
||||
status_code: 200
|
||||
register: login
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
auth_token: "{{ login.json.auth_token }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- name: Get requester user attributes
|
||||
uri:
|
||||
url: "{{ endpoint }}/api/users/{{ user_id }}"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
status_code: 200
|
||||
register: user
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
requester_email: "{{ user.json.email }}"
|
||||
when: manageiq is defined and user.json.email is not none # cloudforms admin user has no email address (unless set), this is a null field in json
|
||||
|
||||
- set_fact:
|
||||
#requester_email: "{{ from_email }}" # from_email should be able to recieve mail via relay
|
||||
requester_email: ucats@exmail.nottingham.ac.uk
|
||||
when: requester_email is not defined
|
||||
|
||||
- set_fact:
|
||||
requester_user: "{{ user.json.name }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# when run from the command line set a default requester user
|
||||
- set_fact:
|
||||
requester_user: "command line invocation"
|
||||
when: manageiq is not defined
|
||||
|
||||
# use when we need to determine if this is a self service request - might not be used for this playbook
|
||||
# - name: get AD account name
|
||||
# set_fact:
|
||||
# requester_user_ad: "{{ (user.json.email).split('@')[0] }}"
|
||||
# when: manageiq is defined
|
||||
|
||||
- name: get service
|
||||
uri:
|
||||
url: "{{ endpoint }}/api/services/{{ service_id }}"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
status_code: 200
|
||||
register: service
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
service_name: "{{ service.json.name }}"
|
||||
new_service_name: "{{ service.json.name }} {{ request_id }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
service_name: "command line invocation"
|
||||
new_service_name: "command line invocation"
|
||||
when: manageiq is not defined
|
||||
|
||||
- name: set service name
|
||||
uri:
|
||||
url: "{{ endpoint }}/api/services/{{ service_id }}"
|
||||
validate_certs: no
|
||||
method: POST
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
body_format: json
|
||||
body: { "action" : "edit", "resource" : { "name" : "{{ new_service_name }}" }}
|
||||
status_code: 200, 204
|
||||
register: service
|
||||
when: manageiq is defined
|
||||
|
||||
|
||||
# PLAY
|
||||
# Validate parameters passed to script from cloudforms
|
||||
- name: Script input Validation
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
vars:
|
||||
ownerlist: []
|
||||
groupmemberslist: []
|
||||
groupmemberslistvalidate: []
|
||||
|
||||
tasks:
|
||||
|
||||
# add task to check + fail for any variables with name placeholder here
|
||||
|
||||
- name: Convert owner to list
|
||||
set_fact:
|
||||
ownerlist: "{{ ownerlist }} + [ '{{ item }}' ]"
|
||||
with_items: "{{ owner }}"
|
||||
|
||||
- name: Split groupmembers parameter on , delimiter
|
||||
set_fact:
|
||||
groupmemberslist: "{{ groupmemberslist }} + [ '{{ item }}' ]"
|
||||
with_items: "{{ groupmembers.split(',') }}"
|
||||
|
||||
- name: Remove empty fields from groupmembers parameter
|
||||
set_fact:
|
||||
groupmemberslistvalidate: "{{ groupmemberslistvalidate }} + [ '{{ item }}' ]"
|
||||
when: item | length != 0
|
||||
with_items: "{{ groupmemberslist }}"
|
||||
|
||||
|
||||
# PLAY
|
||||
# Add winrm host used for AD querys to inventory
|
||||
- name: Build inventory for AD server
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Add host entry for adserver
|
||||
add_host: >
|
||||
name=adserver
|
||||
groups=windows
|
||||
ansible_host="{{ ad_host }}"
|
||||
|
||||
|
||||
# PLAY
|
||||
# Query winrm to validate AD user/group exist, build dict for share ACL and dict of all user/email from a recursively search of user/group
|
||||
- name: Check AD user exists
|
||||
hosts: adserver
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
vars:
|
||||
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
||||
requester_email: "{{ hostvars['localhost']['requester_email'] }}"
|
||||
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
||||
ownerlist: "{{ hostvars['localhost']['ownerlist'] }}"
|
||||
groupmemberslistvalidate: "{{ hostvars['localhost']['groupmemberslistvalidate'] }}"
|
||||
ownermembers: "{{ ownerlist + groupmemberslistvalidate }}"
|
||||
no_aduser: []
|
||||
object_type: []
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Add connectivity variables for adserver
|
||||
set_fact:
|
||||
ansible_user: "{{ ad_user }}"
|
||||
ansible_password: "{{ ad_pass }}"
|
||||
ansible_connection: "{{ ad_connection }}"
|
||||
ansible_winrm_transport: "{{ ad_winrm_transport }}"
|
||||
ansible_winrm_kinit_mode: "{{ ad_winrm_kinit_mode }}"
|
||||
ansible_winrm_message_encryption: "{{ ad_winrm_message_encryption }}"
|
||||
ansible_port: "{{ ad_port }}"
|
||||
ansible_winrm_scheme: "{{ ad_winrm_scheme }}"
|
||||
ansible_winrm_server_cert_validation: "{{ ad_winrm_server_cert_validation }}"
|
||||
# ansible_winrm_operation_timeout_sec: 60
|
||||
# ansible_winrm_read_timeout_sec: 60
|
||||
|
||||
- name: Check AD user/group exists
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne()
|
||||
register: command_result
|
||||
with_items:
|
||||
# - "{{ ownerlist }}"
|
||||
# - "{{ groupmemberslistvalidate }}"
|
||||
- "{{ ownermembers }}"
|
||||
|
||||
- name: Flag fail where AD user/group not exist
|
||||
set_fact:
|
||||
no_aduser: "{{ no_aduser + [item.item] }}"
|
||||
when: item.stdout | length == 0
|
||||
with_items: "{{ command_result.results }}"
|
||||
changed_when: true
|
||||
notify: topic_noad
|
||||
|
||||
- name: Check AD object is user or group
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item.item }})").FindOne().Properties.objectcategory
|
||||
register: object_result
|
||||
with_items: "{{ command_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Build list of AD object type
|
||||
set_fact:
|
||||
object_type: "{{ object_type + [(item.stdout.split(',')[0].split('CN=')[1])] }}" # faster than regex
|
||||
with_items: "{{ object_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# cheat, the first entry in the list ownermember (user or group) is the owner where we merge ownerlist + groupmemberslistvalidate
|
||||
# for this to work the owner list must have only one entry
|
||||
# to use this logic for groups of objects (i.e rwx, rw, r groups), we will need to merge a dict with fields name, role and the list object_attributes
|
||||
- name: Build dict of object names, types and roles positionally from list with object name and list with object type
|
||||
set_fact:
|
||||
object_attributes: "{{ object_attributes | default([]) + [dict(name=item[0], type=item[1], role='owner' if object_attributes is undefined else 'member') ] }}"
|
||||
loop: "{{ ownermembers|zip(object_type)|list }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ object_attributes }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
# recursive loop control using include_tasks and block rescue behaviour, https://github.com/ansible/ansible/issues/46203
|
||||
# write/access variables between plays using dummy host variables, https://www.unixarena.com/2019/05/passing-variable-from-one-playbook-to-another-playbook-ansible.html/
|
||||
|
||||
- name: Register dummy host with variable object_attributes
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
object_attributes: "{{ object_attributes }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Find all group members
|
||||
include_tasks: group_lookup.yml
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - name: Inspect all users who will require email notification
|
||||
# debug:
|
||||
# msg: "{{ hostvars['DUMMY_HOST']['find_users'] }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
- name: Import dummy host variable from group_lookup.yml
|
||||
set_fact:
|
||||
unique_users: "{{ hostvars['DUMMY_HOST']['find_users'] }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Get unique name/role/type entries, remove duplicate entries that may arise from group nesting
|
||||
set_fact:
|
||||
unique_users: "{{ unique_users | unique }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ unique_users }}"
|
||||
|
||||
- name: Get owner names into a list
|
||||
set_fact:
|
||||
owner_users: "{{ owner_users | default([]) + [item.name] }}"
|
||||
with_items: "{{ unique_users }}"
|
||||
when: no_aduser | length == 0 and item.role == 'owner'
|
||||
|
||||
- name: Get member names into a list
|
||||
set_fact:
|
||||
member_users: "{{ member_users | default([]) + [item.name] }}"
|
||||
with_items: "{{ unique_users }}"
|
||||
when: no_aduser | length == 0 and item.role == 'member'
|
||||
|
||||
- name: Get names common in both lists
|
||||
set_fact:
|
||||
common_users: "{{ owner_users | intersect(member_users) }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ common_users }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
# KEEP THIS
|
||||
# this is a dedupe task where duplicate users exist one with role owner and one with role member, the user with role member is removed
|
||||
# this isnt too helpful in gpfs as the owner attribute is the unix owner of the fileset mount point not a windows ACL
|
||||
# the logic is useful in a permissions scenario - example multiple entries for a user with rwx and r-- permissions, we want to ensure only the rwx role is used
|
||||
# - name: Remove member entries where competing owner entry exists
|
||||
# set_fact:
|
||||
# email_users: "{{ email_users | default([]) + [dict(name=item.name, role=item.role, type=item.type)] }}"
|
||||
# with_items: "{{ unique_users }}"
|
||||
# when: no_aduser | length == 0 and (item.name not in common_users or (item.name in common_users and item.role == 'owner'))
|
||||
- set_fact:
|
||||
email_users: "{{ unique_users }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# final deduplicated dict to send appropriate class of email to users
|
||||
# - debug:
|
||||
# msg: "{{ email_users }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
- name: Get member email address from AD
|
||||
win_shell: Get-ADUser {{ item.name }} -Properties mail | Select-Object -ExpandProperty mail
|
||||
register: email_result
|
||||
with_items: "{{ email_users }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ email_result }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
# this will crash out where customer account has no associated email, UoN are very consistent with account creation and adding email - needs logic for empty elements
|
||||
- name: Get member emails into a list
|
||||
set_fact:
|
||||
email_address: "{{ email_address | default([]) + [item.stdout_lines[0]] }}"
|
||||
with_items: "{{ email_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Build dict of object name, role, type and email
|
||||
set_fact:
|
||||
user_dict: "{{ user_dict | default([]) + [dict(name=item[0].name, role=item[0].role, type=item[0].type, email=item[1])] }}" # adding a new positional field from the list to the dict
|
||||
loop: "{{ email_users|zip(email_address)|list }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# COMMENT OUT
|
||||
- debug:
|
||||
msg: "{{ user_dict }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
handlers:
|
||||
|
||||
- name: Build invalid user(s) email body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/mail-invalid.j2') }}"
|
||||
delegate_to: localhost
|
||||
listen:
|
||||
- topic_noad
|
||||
|
||||
- name: Send status email
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ requester_email }}"
|
||||
subject: "Result of request {{ body_service_name }}"
|
||||
body: "{{ mail_body }}"
|
||||
delegate_to: localhost
|
||||
when: requester_email is defined and enable_requester_email
|
||||
listen:
|
||||
- topic_noad
|
||||
|
||||
- name: Condition fail on invalid AD user
|
||||
debug:
|
||||
msg:
|
||||
- "invalid AD user(s)/groups(s)"
|
||||
- ---------------------------------------
|
||||
- "{{ no_aduser }}"
|
||||
- ---------------------------------------
|
||||
delegate_to: localhost
|
||||
listen:
|
||||
- topic_noad
|
||||
|
||||
- name: Hard exit
|
||||
fail:
|
||||
listen:
|
||||
- topic_noad
|
||||
|
||||
|
||||
# PLAY
|
||||
# Create GPFS fileset with quota, samba export and accociated ACLs
|
||||
- name: GPFS create fileset and samba export with quota
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
- vars/requests.yml
|
||||
vars:
|
||||
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
||||
requester_email: "{{ hostvars['localhost']['requester_email'] }}"
|
||||
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
||||
|
||||
tasks:
|
||||
|
||||
# task here to work out quota size as a parameter
|
||||
|
||||
- name: Check samba share exists
|
||||
include_tasks: tasks/checkfilesetsambaexport.yml
|
||||
|
||||
- name: Check fileset exists
|
||||
include_tasks: tasks/checkfileset.yml
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined
|
||||
|
||||
- name: Create fileset
|
||||
include_tasks: tasks/createfileset.yml
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined
|
||||
|
||||
- name: Create fileset quota
|
||||
include_tasks: tasks/createfilesetquota.yml
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined
|
||||
|
||||
- name: Create samba share
|
||||
include_tasks: tasks/createsambaexport.yml
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined
|
||||
|
||||
# if the ACL apply fails the loop terminates, users following a failed user ACL will not be processed, this shouldnt happen but there is no control for this
|
||||
# would be good to check share exists already like the following remove task, would help if the script needs to update ACL
|
||||
- name: Create samba share ACL
|
||||
include_tasks: tasks/createsambaacl.yml
|
||||
vars:
|
||||
ad_object: "{{ item.name }}"
|
||||
with_items: "{{ hostvars['adserver']['object_attributes'] }}"
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined and item.role == 'member' # remember KEEP THIS comment under hosts: adserver
|
||||
|
||||
- name: Remove samba share ACL
|
||||
include_tasks: tasks/removesambaacl.yml
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined
|
||||
|
||||
- name: List samba share ACL
|
||||
include_tasks: tasks/listsambaacl.yml
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is not defined
|
||||
|
||||
- name: Report storage failure
|
||||
set_fact:
|
||||
storage_fail: "{{ hostvars['DUMMY_HOST']['storage_fail'] }}"
|
||||
when: hostvars['DUMMY_HOST']['storage_fail'] is defined
|
||||
changed_when: true
|
||||
notify: topic_fail
|
||||
|
||||
# - name: Get members for output
|
||||
# set_fact:
|
||||
# notify_members: "{{ notify_members | default([]) + [dict(name=item.name, email=item.email)]}}"
|
||||
# with_items: "{{ hostvars['adserver']['user_dict'] }}"
|
||||
# when: hostvars['DUMMY_HOST']['acl_entry'] is defined and (item.role == 'member' and item.type == 'Person')
|
||||
|
||||
# - name: Report ACL entry
|
||||
# set_fact:
|
||||
# acl_entry: "{{ hostvars['DUMMY_HOST']['acl_entry'] }}"
|
||||
# when: hostvars['DUMMY_HOST']['acl_entry'] is defined
|
||||
# changed_when: true
|
||||
# notify: topic_pass
|
||||
|
||||
handlers:
|
||||
|
||||
- name: Build fail on storage provision email body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/mail-storage-fail.j2') }}"
|
||||
listen:
|
||||
- topic_fail
|
||||
|
||||
- name: Send fail email
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ requester_email }}"
|
||||
subject: "Result of request {{ body_service_name }}"
|
||||
body: "{{ mail_body }}"
|
||||
when: requester_email is defined and enable_requester_email
|
||||
listen:
|
||||
- topic_fail
|
||||
|
||||
- name: Condition fail on provision storage
|
||||
debug:
|
||||
msg:
|
||||
- "provisioning storage failure"
|
||||
- ---------------------------------------
|
||||
- "{{ storage_fail }}"
|
||||
- ---------------------------------------
|
||||
listen:
|
||||
- topic_fail
|
||||
|
||||
- name: Hard exit
|
||||
fail:
|
||||
listen:
|
||||
- topic_fail
|
||||
|
||||
# PLAY
|
||||
# Console and email output of results
|
||||
- name: Report results
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
vars:
|
||||
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
||||
requester_email: "{{ hostvars['localhost']['requester_email'] }}"
|
||||
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
||||
|
||||
tasks:
|
||||
|
||||
# - name: Report storage failure
|
||||
# set_fact:
|
||||
# storage_fail: "{{ hostvars['DUMMY_HOST']['storage_fail'] }}"
|
||||
# when: hostvars['DUMMY_HOST']['storage_fail'] is defined
|
||||
# changed_when: true
|
||||
# notify: topic_fail
|
||||
|
||||
- name: Get members name/email for notification requester email body
|
||||
set_fact:
|
||||
notify_members: "{{ notify_members | default([]) + [dict(name=item.name, email=item.email)]}}"
|
||||
with_items: "{{ hostvars['adserver']['user_dict'] }}"
|
||||
when: hostvars['DUMMY_HOST']['acl_entry'] is defined and (item.role == 'member' and item.type == 'Person')
|
||||
|
||||
- name: Get member email to list for email module
|
||||
set_fact:
|
||||
notify_customers: "{{ notify_customers | default([]) + [item.email]}}"
|
||||
with_items: "{{ hostvars['adserver']['user_dict'] }}"
|
||||
when: hostvars['DUMMY_HOST']['acl_entry'] is defined and (item.role == 'member' and item.type == 'Person')
|
||||
|
||||
- name: Report ACL entry
|
||||
set_fact:
|
||||
acl_entry: "{{ hostvars['DUMMY_HOST']['acl_entry'] }}"
|
||||
when: hostvars['DUMMY_HOST']['acl_entry'] is defined
|
||||
changed_when: true
|
||||
notify: topic_pass
|
||||
|
||||
handlers:
|
||||
|
||||
- name: Build requester storage provision email body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/mail-storage-requester.j2') }}"
|
||||
listen:
|
||||
- topic_pass
|
||||
|
||||
- name: Send requester storage provision email body
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ requester_email }}"
|
||||
subject: "Result of request {{ body_service_name }}"
|
||||
body: "{{ mail_body }}"
|
||||
when: requester_email is defined and enable_requester_email
|
||||
listen:
|
||||
- topic_pass
|
||||
|
||||
- name: Build member storage provision email body
|
||||
set_fact:
|
||||
mail_body: "{{ lookup('template', 'templates/mail-storage-customer.j2') }}"
|
||||
listen:
|
||||
- topic_pass
|
||||
|
||||
- name: Send member storage provision email body
|
||||
mail:
|
||||
host: "{{ smtp_relay }}"
|
||||
port: "{{ smtp_port }}"
|
||||
charset: utf-8
|
||||
from: "{{ from_email }}"
|
||||
to: "{{ notify_customers }}"
|
||||
subject: "Result of request {{ body_service_name }}"
|
||||
body: "{{ mail_body }}"
|
||||
when: enable_customer_email
|
||||
listen:
|
||||
- topic_pass
|
||||
|
||||
- name: Report "{{ filesetName }}" sucessful creation and applied ACL
|
||||
debug:
|
||||
msg:
|
||||
- "storage {{ filesetName }} sucessfully created"
|
||||
- ------------------------------------------------------------------------------
|
||||
- "access share @UNC path //{{ clusterapiIP }}/{{ filesetName }}"
|
||||
- ------------------------------------------------------------------------------
|
||||
- "applied ACL for {{ filesetName }}"
|
||||
- ------------------------------------------------------------------------------
|
||||
- "{{ acl_entry }}"
|
||||
- ------------------------------------------------------------------------------
|
||||
- "users with access to the share that will recieve email notification"
|
||||
- ------------------------------------------------------------------------------
|
||||
- "{{ notify_members }}"
|
||||
listen:
|
||||
- topic_pass
|
||||
|
||||
|
||||
|
||||
# TEST + BUILD
|
||||
# need input placeholder validation
|
||||
# need a maximum size of share as a parameter, this will enable different tiles/tag combinations for users/groups
|
||||
# may change with netapp module - use a different play to report + email - is there any point? maybe with netapp + dual functionality (play for netapp, play for gpfs) - use some more handlers and the exisitng customer emails switch
|
||||
|
||||
# NOT UNTIL MORE REQUIREMENTS
|
||||
# owner as a group - no need today - this is a unix permission, maybe best set to service_cloudforms or even local root of GPFS
|
||||
# dont think i need to worry about 'you are already member of' regarding ACL's - we are creating new storage - no requirement to delete storage yet - can put in check it will help for update/add permissions
|
||||
|
||||
# nice jmespath check - clean
|
||||
# - name: query results of output files exist on remote host
|
||||
# set_fact:
|
||||
# query_certs: "{{ stat_result | json_query(jmesquery) }}"
|
||||
# vars:
|
||||
# jmesquery: "results[?(@.stat.exists)].item"
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
- name: Check fileset exists
|
||||
uri:
|
||||
url: "{{ checkfilesetEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
force_basic_auth: yes
|
||||
status_code: 200, 400
|
||||
register: response
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "{{ filesetName }} already exists, please choose another share name"
|
||||
# when: response.json.status.code == 200
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "{{ filesetName }} fileset already exists, please choose another share name"
|
||||
when: response.json.status.code == 200
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
- name: Check share exists
|
||||
uri:
|
||||
url: "{{ checkfilesetsambaexportEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
force_basic_auth: yes
|
||||
status_code: 200, 400
|
||||
register: response
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "{{ filesetName }} already exists, please choose another share name"
|
||||
# when: response.json.status.code == 200
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "{{ filesetName }} share already exists, please choose another share name"
|
||||
when: response.json.status.code == 200
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
- name: Check job
|
||||
uri:
|
||||
url: "{{ checkjobEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
body_format: json
|
||||
force_basic_auth: yes
|
||||
register: jobresponse
|
||||
until: jobresponse.json.jobs[0].status != 'RUNNING'
|
||||
retries: 20
|
||||
delay: 10
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg:
|
||||
# - "{{ fail_message }}"
|
||||
# - "{{ jobresponse.json.jobs[0].result.stderr }}"
|
||||
# when: jobresponse.json.jobs[0].status == 'FAILED'
|
||||
|
||||
# - name: Check job id and endpoint
|
||||
# debug:
|
||||
# msg:
|
||||
# - "{{ jobId }}"
|
||||
# - "{{ checkjobEndpoint }}"
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "{{ fail_message }}, API output {{ jobresponse.json.jobs[0].result.stderr }}"
|
||||
when: jobresponse.json.jobs[0].status == 'FAILED'
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
- name: Create fileset
|
||||
uri:
|
||||
url: "{{ createfilesetEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: POST
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
body_format: json
|
||||
body: "{{ createfileset }}"
|
||||
force_basic_auth: yes
|
||||
status_code: 202, 400
|
||||
register: response
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "Invalid request body"
|
||||
# when: response.json.status.code == 400
|
||||
|
||||
# - set_fact:
|
||||
# jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
|
||||
# - name: Check Job
|
||||
# include_tasks: checkjob.yml
|
||||
# vars:
|
||||
# failmessage: "Unable to create fileset"
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "Create fileset invalid request body, please notify administrator"
|
||||
when: response.json.status.code == 400
|
||||
|
||||
- set_fact:
|
||||
jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
when: response.json.status.code != 400
|
||||
|
||||
- name: Check Job
|
||||
include_tasks: checkjob.yml
|
||||
vars:
|
||||
fail_message: "Unable to create fileset"
|
||||
when: response.json.status.code != 400
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
- name: Create fileset quota
|
||||
uri:
|
||||
url: "{{ createfilesetquotaEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: POST
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
body_format: json
|
||||
body: "{{ quotafileset }}"
|
||||
force_basic_auth: yes
|
||||
status_code: 202, 400
|
||||
register: response
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "Invalid request body"
|
||||
# when: response.json.status.code == 400
|
||||
|
||||
# - set_fact:
|
||||
# jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
|
||||
# - name: Check Job
|
||||
# include_tasks: checkjob.yml
|
||||
# vars:
|
||||
# failmessage: "Unable to create fileset quota"
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "Create fileset quota invalid request body, please notify administrator"
|
||||
when: response.json.status.code == 400
|
||||
|
||||
- set_fact:
|
||||
jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
when: response.json.status.code != 400
|
||||
|
||||
- name: Check Job
|
||||
include_tasks: checkjob.yml
|
||||
vars:
|
||||
fail_message: "Unable to create fileset quota"
|
||||
when: response.json.status.code != 400
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
# GPFS AD connectivity/cache may not be able to return a user lookup from AD in a timely manner after a period of inactivity, until loop employed
|
||||
- name: Create fileset samba export ACL
|
||||
uri:
|
||||
url: "{{ createsambaaclEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: PUT
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
body_format: json
|
||||
body: "{{ smbexportfilesetacl }}"
|
||||
force_basic_auth: yes
|
||||
status_code: 202, 400
|
||||
register: response
|
||||
until: response.json.status.code == 202
|
||||
retries: 20
|
||||
delay: 10
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "Invalid request body"
|
||||
# when: response.json.status.code == 400
|
||||
|
||||
# - set_fact:
|
||||
# jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
|
||||
# - name: Check Job
|
||||
# include_tasks: checkjob.yml
|
||||
# vars:
|
||||
# failmessage: "Unable to apply ACL to share {{ filesetName }}"
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "Create share ACL, invalid request body or GPFS->AD connectivity issue, please notify administrator"
|
||||
when: response.json.status.code == 400
|
||||
|
||||
- set_fact:
|
||||
jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
when: response.json.status.code != 400
|
||||
|
||||
- name: Check Job
|
||||
include_tasks: checkjob.yml
|
||||
vars:
|
||||
fail_message: "Unable to apply ACL to share"
|
||||
when: response.json.status.code != 400
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
- name: Create fileset samba export
|
||||
uri:
|
||||
url: "{{ createsambaexportEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: POST
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
body_format: json
|
||||
body: "{{ smbexportfileset }}"
|
||||
force_basic_auth: yes
|
||||
status_code: 202, 400
|
||||
register: response
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "Invalid request body"
|
||||
# when: response.json.status.code == 400
|
||||
|
||||
# - set_fact:
|
||||
# jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
|
||||
# - name: Check Job
|
||||
# include_tasks: checkjob.yml
|
||||
# vars:
|
||||
# failmessage: "Unable to create share"
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "Create share invalid request body, please notify administrator"
|
||||
when: response.json.status.code == 400
|
||||
|
||||
- set_fact:
|
||||
jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
when: response.json.status.code != 400
|
||||
|
||||
- name: Check Job
|
||||
include_tasks: checkjob.yml
|
||||
vars:
|
||||
fail_message: "Unable to create share"
|
||||
when: response.json.status.code != 400
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
- name: List fileset samba export ACL
|
||||
uri:
|
||||
url: "{{ listsambaaclEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
force_basic_auth: yes
|
||||
status_code: 200, 202, 400
|
||||
register: response
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "List share ACL invalid request body, please notify administrator"
|
||||
when: response.json.status.code == 400
|
||||
|
||||
- name: Register dummy host with variable acl_entry
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
acl_entry: "{{ response.json.smbAclEntries }}"
|
||||
when: response.json.status.code == 200
|
||||
|
||||
# - debug:
|
||||
# msg:
|
||||
# - "{{ response.json.smbAclEntries }}"
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
- name: Check fileset samba export default ACL
|
||||
uri:
|
||||
url: "{{ checksambaeveryoneaclEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
force_basic_auth: yes
|
||||
status_code: 200, 400
|
||||
register: check_response
|
||||
|
||||
# - name: Inspect remove ACL check
|
||||
# debug:
|
||||
# msg: "{{ check_response }}"
|
||||
|
||||
# GPFS AD connectivity/cache may not be able to return a user lookup from AD in a timely manner after a period of inactivity, until loop employed
|
||||
- name: Remove fileset samba export default ACL
|
||||
uri:
|
||||
url: "{{ removesambaeveryoneaclEndpoint }}"
|
||||
user: "{{ clusterUser }}"
|
||||
password: "{{ clusterPassword }}"
|
||||
method: DELETE
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
force_basic_auth: yes
|
||||
status_code: 202, 400
|
||||
register: response
|
||||
until: response.json.status.code == 202
|
||||
retries: 20
|
||||
delay: 10
|
||||
when: check_response.json.status.code == 200
|
||||
|
||||
# - name: Inspect remove ACL check
|
||||
# debug:
|
||||
# msg: "{{ response }}"
|
||||
|
||||
# - name: Report failure
|
||||
# fail:
|
||||
# msg: "Invalid request body"
|
||||
# when: response.json.status.code == 400
|
||||
|
||||
# - set_fact:
|
||||
# jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
|
||||
# - name: Check Job
|
||||
# include_tasks: checkjob.yml
|
||||
# vars:
|
||||
# failmessage: "Unable to remove ACL for group Everyone for share {{ filesetName }}"
|
||||
|
||||
- name: Register dummy host with variable storage_fail
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
storage_fail: "Remove share ACL invalid request body, please notify administrator"
|
||||
#when: not response.skipped and response.json.status.code == 400
|
||||
when: response.json.status.code == 400
|
||||
|
||||
- set_fact:
|
||||
jobId: "{{ response.json.jobs[0].jobId }}"
|
||||
#when: not response.skipped and response.json.status.code != 400
|
||||
when: response.json.status.code != 400
|
||||
|
||||
- name: Check Job
|
||||
include_tasks: checkjob.yml
|
||||
vars:
|
||||
fail_message: "Unable to remove 'group Everybody' ACL for share"
|
||||
#when: not response.skipped and response.json.status.code != 400
|
||||
when: response.json.status.code != 400
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
invalid AD user(s);
|
||||
|
||||
{% for entry in no_aduser %}
|
||||
{{ entry }}
|
||||
{% endfor %}
|
||||
|
||||
job failed.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
you have been added to storage;
|
||||
|
||||
{{ filesetName }}
|
||||
|
||||
to access this share navigate to the following UNC path and provide university credentials;
|
||||
|
||||
//{{ clusterapiIP }}/{{ filesetName }}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
failed to provision storage;
|
||||
|
||||
{{ storage_fail }}
|
||||
|
||||
job failed.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
Service request: {{ body_service_name }}
|
||||
Requester: {{ body_requester_user }}
|
||||
Result:
|
||||
|
||||
storage sucessfully provisioned;
|
||||
|
||||
{{ filesetName }}
|
||||
|
||||
to access this share navigate to the following UNC path and provide university credentials;
|
||||
|
||||
//{{ clusterapiIP }}/{{ filesetName }}
|
||||
|
||||
ACL applied to share;
|
||||
|
||||
{{ acl_entry }}
|
||||
|
||||
users with access to the share;
|
||||
|
||||
{{ notify_members }}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
# CES ip/credentials
|
||||
clusterapiIP: "51.132.24.66"
|
||||
clusterUser: ocfadmin
|
||||
clusterPassword: mnBMghZLWg63Kge2
|
||||
|
||||
# CES filesystem mount attributes
|
||||
clustermountPrefix: gpfs # /gpfs/fs1 mount point on the file system, attribute required for smb exports
|
||||
filesystemName: fs1 # filesystem, probably easiest to create a filesystem (with quotas enabled) for this script
|
||||
inodeSpace: root # dependent inode space used, pick a fileset as the parent inode set, the root fileset created for every filesystem is acceptable
|
||||
|
||||
# CES fileset attributes, used to define new fileset with quota and samba export (samba export shares name with fileset), these are to be script parameters
|
||||
filesetName: test6
|
||||
quotasizeUnit: G
|
||||
quotahardPercentage: 10
|
||||
quotasoftSize: 10.5
|
||||
quotahardSize: "{{ ((quotasoftSize / 100 * quotahardPercentage) + quotasoftSize)|round(1,'ceil')|abs }}" # quota hard limit, acceptable for M G T sizes
|
||||
|
||||
# CES Unix directory ownership (might be hardcoded as not useful for samba)
|
||||
#owner: uizrs
|
||||
#owner: ui-cloudforms-dev
|
||||
owner: service_CloudForms
|
||||
|
||||
# CES AD users/groups for samba ACL and email notification
|
||||
#groupmembers: service_CloudForms,service_CloudForms
|
||||
#groupmembers: ucats,ucasw2,uizrs,bhzajd,ui-cloudforms-dev
|
||||
groupmembers: ucats
|
||||
#groupmembers: ui-cloudforms-dev
|
||||
#groupmembers: ui-cloudforms-dev
|
||||
#groupmembers: ucats,ucasw2
|
||||
#groupmembers: ui-thirdpartysupport-essential
|
||||
|
||||
# AD winrm connectivity details
|
||||
ad_host: uiwdcjub04.ad.nottingham.ac.uk # active directory server, when using kerberos (with requisite resolv.conf entry) this must be a fqdn
|
||||
#ad_host: UIWDCUPK06.nottingham.ac.uk # works local+cf
|
||||
# loadbalanced kerberos doesnt really work unless there are correct entries and ptr records for the loadbalancer endpoint
|
||||
#ad_host: cfrm.ad.nottingham.ac.uk
|
||||
#ad_host: uivlan913vip3.nottingham.ac.uk
|
||||
# ad_host: 128.243.226.17
|
||||
# members of cfrm.nottingham.ac.uk
|
||||
#ad_host: uiwdcdns07.ad.nottingham.ac.uk # works
|
||||
#ad_host: uiwdcjub04a.ad.nottingham.ac.uk # dns not in kerberos db uiwdcjub04a.ad.nottingham.ac.uk - this maybe arogue PTR record
|
||||
#ad_host: uiwdcjub04a.ad.nottingham.ac.uk # works
|
||||
#ad_host: uiwdcupk06.ad.nottingham.ac.uk # works, random timeouts over vpn
|
||||
#
|
||||
ad_user: "service_CloudForms" #"service_cloudforms" # AD service account capable of manipulating group membership
|
||||
ad_pass: "As109pHY4Wi9o7naZnhr#!" # AD service account password
|
||||
ad_connection: winrm
|
||||
ad_winrm_transport: kerberos
|
||||
ad_winrm_kinit_mode: managed # allow ansible to manage own kerberos token, this will use credentials to make token entries required
|
||||
ad_winrm_message_encryption: auto # can be set to always, depends on ad server profile
|
||||
ad_port: 5986 # 5985/http for non https transport, UON on-prem use 5986/https
|
||||
ad_winrm_scheme: https # UON on-prem use 5986/https
|
||||
ad_winrm_server_cert_validation: ignore
|
||||
|
||||
# control email recipients
|
||||
enable_requester_email: true
|
||||
enable_customer_email: true
|
||||
#from_email: placeholder # donotreply@nottingham.ac.uk
|
||||
from_email: donotreply@nottingham.ac.uk
|
||||
|
||||
# smtp server
|
||||
smtp_relay: smtp.nottingham.ac.uk
|
||||
smtp_port: 25
|
||||
|
||||
# Cloudforms API
|
||||
api_user: placeholder
|
||||
api_pass: placeholder
|
||||
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
# swagger API explorer: https://{{ clusterapiIP }}/ibm/api/explorer/#!/Spectrum_Scale_REST_API_v2/
|
||||
#
|
||||
checkfilesetsambaexportEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}"
|
||||
checkfilesetEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/filesystems/{{ filesystemName }}/filesets/{{ filesetName }}"
|
||||
createfilesetEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/filesystems/{{ filesystemName }}/filesets"
|
||||
checkjobEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/jobs/{{ jobId }}"
|
||||
createfilesetquotaEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/filesystems/{{ filesystemName }}/quotas"
|
||||
createsambaexportEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares"
|
||||
#createsambaaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl/{{ owner }}"
|
||||
createsambaaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl/{{ ad_object }}"
|
||||
#checksambaeveryoneaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl/\\Everyone" # %5C
|
||||
#removesambaeveryoneaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl/\\Everyone" # %5C
|
||||
checksambaeveryoneaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl/%5CEveryone"
|
||||
removesambaeveryoneaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl/%5CEveryone"
|
||||
listsambaaclEndpoint: "https://{{ clusterapiIP }}:443/scalemgmt/v2/smb/shares/{{ filesetName }}/acl"
|
||||
# createfileset: {
|
||||
# "filesetName": "{{ filesetName }}",
|
||||
# "path": "/{{ clustermountPrefix }}/{{ filesystemName }}/{{ filesetName }}",
|
||||
# "createDirectory": true,
|
||||
# "owner": "{{ inodeSpace }}",
|
||||
# "permissions": 755,
|
||||
# "inodeSpace": "{{ inodeSpace }}"
|
||||
# }
|
||||
createfileset: {
|
||||
"filesetName": "{{ filesetName }}",
|
||||
"path": "/{{ clustermountPrefix }}/{{ filesystemName }}/{{ filesetName }}",
|
||||
"createDirectory": true,
|
||||
"permissions": 755,
|
||||
"inodeSpace": "{{ inodeSpace }}"
|
||||
}
|
||||
quotafileset: {
|
||||
"operationType": "setQuota",
|
||||
"quotaType": "FILESET",
|
||||
"objectName": "{{ filesetName }}",
|
||||
"blockSoftLimit": "{{ quotasoftSize }}{{ quotasizeUnit }}",
|
||||
"blockHardLimit": "{{ quotahardSize }}{{ quotasizeUnit }}"
|
||||
}
|
||||
smbexportfileset: {
|
||||
"shareName": "{{ filesetName }}",
|
||||
"path": "/{{ clustermountPrefix }}/{{ filesystemName }}/{{ filesetName }}",
|
||||
"smbOptions": {
|
||||
"browseable": "yes",
|
||||
"smbEncrypt": "auto",
|
||||
"comment": "Provisioned by Cloudforms",
|
||||
"cscPolicy": "manual",
|
||||
"fileIdAlgorithm": "fsname",
|
||||
"gpfsLeases": "yes",
|
||||
"gpfsRecalls": "yes",
|
||||
"gpfsShareModes": "yes",
|
||||
"gpfsSyncIo": "no",
|
||||
"hideUnreadable": "no",
|
||||
"opLocks": "yes",
|
||||
"posixLocking": "no",
|
||||
"readOnly": "no",
|
||||
"syncOpsOnClose": "no",
|
||||
"hideDotFiles": "no"
|
||||
}
|
||||
}
|
||||
# following request sets fileset owner with unix permissions I believe
|
||||
# smbexportfilesetacl: {
|
||||
# "shareName": "{{ filesetName }}",
|
||||
# "name": "{{ owner }}",
|
||||
# "access": "ALLOWED",
|
||||
# "permissions": "FULL",
|
||||
# "type": "USER"
|
||||
# }
|
||||
smbexportfilesetacl: {
|
||||
"shareName": "{{ filesetName }}",
|
||||
"name": "{{ ad_object }}",
|
||||
"access": "ALLOWED",
|
||||
"permissions": "FULL",
|
||||
"type": "USER"
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
## What is this playbook?
|
||||
|
||||
|
||||
|
||||
It provisions Netapp storage shared over samba.
|
||||
|
||||
|
||||
|
||||
This playbook was a proof of concept for the native ansible netapp modules, this was written at the start of the self provisioning project.
|
||||
|
||||
|
||||
|
||||
The methodology of the playbook is not compatible with the working practises of the UoN storage team, it uses the cluster manager ip not the svm ip, it creates a volume not a qtree.
|
||||
|
||||
|
||||
|
||||
## The playbook demonstrates
|
||||
|
||||
|
||||
|
||||
Some input validation of parameters.
|
||||
|
||||
|
||||
|
||||
Create/Delete a flexvol.
|
||||
|
||||
|
||||
|
||||
Create/Delete a samba share.
|
||||
|
||||
|
||||
|
||||
Apply ACL to samba share.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# Run playbook with parameters passed from Cloudforms:
|
||||
#
|
||||
# Run on command line with parameters where no lists are required:
|
||||
#
|
||||
# ansible-playbook vserver.yml -e 'unique_identifier="some_ref" \
|
||||
# cifs_ad_object="tseed" \
|
||||
# cifs_administrator_object="administrator" \
|
||||
# size=2 \
|
||||
# netapp_hostname="192.168.101.131" \
|
||||
# netapp_username="admin" \
|
||||
# netapp_password="Password0" \
|
||||
# netapp_vserver="netappsim-svm1" \
|
||||
# perform=create'
|
||||
#
|
||||
# Run on command line with parameters as serialised json document where lists are required (cifs_ad_object), size can be omitted from the delete action, dummy ad objects can be used in the delete action
|
||||
#
|
||||
# ansible-playbook vserver.yml -e '{"unique_identifier":"some_ref","cifs_ad_object":["tseed","swright"],"cifs_administrator_object":["administrator","ops"],"size":1,"netapp_hostname":"192.168.101.131","netapp_username":"admin","netapp_password":"Password0","netapp_vserver":"netappsim-svm1","perform":"create"}'
|
||||
# ansible-playbook vserver.yml -e '{"unique_identifier":"some_ref","cifs_ad_object":["tseed"],"cifs_administrator_object":["administrator"],"size":4.1,"netapp_hostname":"192.168.101.131","netapp_username":"admin","netapp_password":"Password0","netapp_vserver":"netappsim-svm1","perform":"create"}'
|
||||
# ansible-playbook vserver.yml -e '{"unique_identifier":"some_ref","cifs_ad_object":["nobody"],"cifs_administrator_object":["nobody"],"netapp_hostname":"192.168.101.131","netapp_username":"admin","netapp_password":"Password0","netapp_vserver":"netappsim-svm1","perform":"delete"}'
|
||||
#
|
||||
---
|
||||
prefix: "CF"
|
||||
unique_identifier: "placeholder" #populated by user to uniquely identify share or CF Ref#
|
||||
cifs_ad_object: "placeholder" #AD username or group
|
||||
cifs_administrator_object: "placeholder" #AD administrator, likely be an Admin group
|
||||
size: 1 #size should be integer, will turn float to int
|
||||
netapp_hostname: "placeholder" #"fqdn or ip"
|
||||
netapp_username: "placeholder" #"cluster manager account such as admin (not vsadmin)"
|
||||
netapp_password: "placeholder" #"cluster manager account password"
|
||||
netapp_vserver: placeholder #svm instance name
|
||||
perform: "placeholder" #"should be create or delete else should abort"
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
name: Create/Delete FlexVol With Cifs Share
|
||||
vars:
|
||||
#set present/absent flag for netapp modules from create/delete values in the perform parameter
|
||||
state: "{{ 'present' if perform == 'create' else ( 'absent' if perform == 'delete' else 'placeholder') }}"
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Convert cif_ad_object to list # use where playbook invoked on command line with single string variables, rather than json input that accepts lists
|
||||
vars:
|
||||
cifs_ad_object: ["cifs_ad_object"]
|
||||
register: cifs_ad_object
|
||||
when: cifs_ad_object is string
|
||||
debug: msg="cifs_ad_object {{ cifs_ad_object }} is not a list of users, converting to list"
|
||||
|
||||
- name: Convert cifs_administrator_object to list # use where playbook invoked on command line with single string variables, rather than json input that accepts lists
|
||||
vars:
|
||||
cifs_administrator_object: ["cifs_administrator_object"]
|
||||
register: cifs_administrator_object
|
||||
when: cifs_administrator_object is string
|
||||
debug: msg="cifs_administrator_object {{ cifs_administrator_object }} is not a list of users, converting to list"
|
||||
|
||||
- name: Fail Where Requisite Vars Not Set
|
||||
fail:
|
||||
msg: "Parameter {{item.key}} has value {{item.value}}, {{item.key}} is required to be passed from Cloudforms"
|
||||
when: item.value == 'placeholder'
|
||||
loop: "{{ lookup('dict', vars ) }}" #vars is a special variable of a list containng all varibales in the playbook
|
||||
|
||||
- name: Get Aggregate Available Space
|
||||
na_ontap_command:
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
command: ['set -showseparator "," -units GB;aggr show -aggregate netapp_sim_01_FC_1 -fields availsize']
|
||||
register: aggavail
|
||||
|
||||
- name: Get Aggregate Size
|
||||
na_ontap_command:
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
command: ['set -showseparator "," -units GB;aggr show -aggregate netapp_sim_01_FC_1 -fields size']
|
||||
register: aggtotal
|
||||
|
||||
- name: Get Aggregate Used Space
|
||||
na_ontap_command:
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
command: ['set -showseparator "," -units GB;aggr show -aggregate netapp_sim_01_FC_1 -fields physical-used']
|
||||
register: aggused
|
||||
|
||||
- name: Get Aggregate Space
|
||||
vars:
|
||||
#remove newline, remove \", get command line output (was dirty with newlines), get first item of list (output in list format), split result by comma and grab 6th field, remove GB (this might be a different size unit)
|
||||
availsizeclean: "{{ aggavail.msg | regex_replace('\n','') | regex_replace('\"','') | regex_findall('(?<=<cli-output>)(.*)(?=</cli-output>)') }}"
|
||||
avail: "{{ availsizeclean[0].split(',')[5] | lower | regex_replace('gb','')}}"
|
||||
totalclean: "{{ aggtotal.msg | regex_replace('\n','') | regex_replace('\"','') | regex_findall('(?<=<cli-output>)(.*)(?=</cli-output>)') }}"
|
||||
total: "{{ totalclean[0].split(',')[5] | lower | regex_replace('gb','')}}"
|
||||
usedclean: "{{ aggused.msg | regex_replace('\n','') | regex_replace('\"','') | regex_findall('(?<=<cli-output>)(.*)(?=</cli-output>)') }}"
|
||||
used: "{{ usedclean[0].split(',')[5] | lower | regex_replace('gb','')}}"
|
||||
#aggstats: ["{{ avail }}", "{{ total }}", "{{ used }}"]
|
||||
#debug: msg="available space {{ avail }}, total space {{ total }}, used space {{ used }}, stats {{ aggstats }}"
|
||||
set_fact:
|
||||
aggstats: ["{{ avail }}", "{{ total }}", "{{ used }}"]
|
||||
|
||||
- name: Test Available Aggregate Disk Space
|
||||
# designed to work with thin provisioned aggregates
|
||||
# should stop creation of volume where aggregate + requested volume size exceed a %utilisation threshold
|
||||
# should set hard limit for size of volume, as a backstop
|
||||
# should stop creation of a volume that exceeds the real size of the aggregate (all you see in this scenario is free space remaining on the volume)
|
||||
vars:
|
||||
threshold: "{{ aggstats[1] | float / 100 * 70 }}"
|
||||
toprovision: "{{ aggstats[2] | float + size | float }}"
|
||||
thicktotal: "{{ aggstats[1] | float }}"
|
||||
#debug: msg="max disk to use {{ threshold }}GB @ 70% utilisation of total disk {{ thicktotal }}GB, disk requested to provision {{ size }}GB, total disk used if share provisioned {{ toprovision }}GB"
|
||||
fail:
|
||||
msg: "provioning new {{ size }}GB volume exceeds 70% threshold capacity of aggregate {{ threshold }}GB, total disk required {{ toprovision }}GB"
|
||||
when: toprovision | float > threshold | float
|
||||
|
||||
- name: Create/Delete FlexVol
|
||||
na_ontap_volume:
|
||||
state: "{{ state }}"
|
||||
#state: absent # now interpolated from the value of perform parameter
|
||||
name: "{{ prefix }}_{{ unique_identifier }}"
|
||||
is_infinite: False
|
||||
aggregate_name: netapp_sim_01_FC_1
|
||||
# module parameter size only accepts integers, we use float to validate space provisioning so we convert here
|
||||
size: "{{ size | int }}"
|
||||
size_unit: gb
|
||||
junction_path: /{{ prefix }}_{{ unique_identifier }}
|
||||
volume_security_style: mixed # in use should nfs shares be required, probably not required for just cifs
|
||||
unix_permissions: 777 # when using mixed security style unix permissions must be set, as this is insecure likely qtrees and allowed hosts would be set for nfs
|
||||
space_guarantee: none # thin provisioning
|
||||
#efficiency_policy: # would need to create a policy to include dedupe and compression
|
||||
vserver: "{{ netapp_vserver }}"
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
|
||||
- name: Create Cifs Share
|
||||
na_ontap_cifs:
|
||||
state: "{{ state }}"
|
||||
share_name: "{{ prefix }}_{{ unique_identifier }}"
|
||||
path: /{{ prefix }}_{{ unique_identifier }}
|
||||
vserver: "{{ netapp_vserver }}"
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
notify:
|
||||
- Remove Everyone User From Cifs Share
|
||||
- Add AD user/group To Cifs Share
|
||||
- Add administrator To Cifs Share
|
||||
|
||||
handlers:
|
||||
|
||||
- name: Remove Everyone User From Cifs Share
|
||||
na_ontap_cifs_acl:
|
||||
state: "absent"
|
||||
share_name: "{{ prefix }}_{{ unique_identifier }}"
|
||||
user_or_group: Everyone
|
||||
vserver: "{{ netapp_vserver }}"
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
|
||||
- name: Add AD user/group To Cifs Share
|
||||
when: state == 'present'
|
||||
na_ontap_cifs_acl:
|
||||
state: "{{ state }}"
|
||||
share_name: "{{ prefix }}_{{ unique_identifier }}"
|
||||
#user_or_group: "{{ cifs_ad_object }}"
|
||||
user_or_group: "{{ item }}"
|
||||
permission: full_control
|
||||
vserver: "{{ netapp_vserver }}"
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
loop: "{{ cifs_ad_object }}"
|
||||
|
||||
- name: Add administrator To Cifs Share
|
||||
when: state == 'present'
|
||||
na_ontap_cifs_acl:
|
||||
state: "{{ state }}"
|
||||
share_name: "{{ prefix }}_{{ unique_identifier }}"
|
||||
#user_or_group: "{{ cifs_administrator_object }}"
|
||||
user_or_group: "{{ item }}"
|
||||
permission: full_control
|
||||
vserver: "{{ netapp_vserver }}"
|
||||
hostname: "{{ netapp_hostname }}"
|
||||
username: "{{ netapp_username }}"
|
||||
password: "{{ netapp_password }}"
|
||||
loop: "{{ cifs_administrator_object }}"
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
## What is this playbook?
|
||||
|
||||
|
||||
|
||||
It provisions a qtree on an existing volume shared over samba, it will then apply ACL by powershell to replicate the manual steps taken by the UoN storage team.
|
||||
|
||||
|
||||
|
||||
The playbook was tested on a development build of a netapp single node cluster with an svm connected to a domain.
|
||||
|
||||
|
||||
|
||||
This playbook is functional but unfinished requiring input validation for cloudforms parameterisation, better error condition reporting and customised UoN branded HTML customer notification email templates.
|
||||
|
||||
|
||||
|
||||
Time was called when the UoN development netapp svm test account roles were unable to grant sufficient permissions to run this playbook, previously the native ansible modules targeted the netapp cluster manager. This playbook should run against the netapp cluster manager with the included API calls with the exception of the DACL cli API endpoints, these could be changed to run against the cluster manager IP OR the ansible netapp module reinstated, included in the play are the original native ansible module cli commands that would replace the DACL API calls for whomever picks up this task.
|
||||
|
||||
|
||||
|
||||
The README_DACL.md contains the commands used over ssh to apply the DACL, these could be also be run over ssh by ansible as an alternative to API calls or the ansible netapp cli module.
|
||||
|
||||
|
||||
|
||||
## The playbook demonstrates
|
||||
|
||||
|
||||
|
||||
Checks cloudforms environment and changes the name of the service to include the request ID to uniquely identify what has been ordered.
|
||||
|
||||
|
||||
|
||||
Check the AD users/groups provided for the share permissions are valid.
|
||||
|
||||
|
||||
|
||||
Builds a list of users/groups who will have access to the samba share.
|
||||
|
||||
|
||||
|
||||
Builds a list of users is nested groups and looks up from AD their associated email address to be used in access notification emails.
|
||||
|
||||
|
||||
|
||||
Creates a qtree on the target volume.
|
||||
|
||||
|
||||
|
||||
Creates a quota for the qtree.
|
||||
|
||||
|
||||
|
||||
Toggles volume quotas off then on to ensure the qtree level quota takes effect.
|
||||
|
||||
|
||||
|
||||
Creates DACL policy for a service account that will later change folder ACL over the samba share.
|
||||
|
||||
|
||||
|
||||
Runs powershell via a windows host to change the folder ACL presented over samba.
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
## DACL cli commands to give a user full permission to the qtree folder
|
||||
|
||||
- These commands are run from the cluster controller via ssh.
|
||||
- To run these commands on the SVM remove the term -vserver netappsim-svm1.
|
||||
|
||||
#### create a policy
|
||||
|
||||
vserver security file-directory policy create -vserver netappsim-svm1 -policy-name myqtree
|
||||
|
||||
#### create and add rules to a security descriptor
|
||||
|
||||
vserver security file-directory ntfs dacl add -vserver netappsim-svm1 -ntfs-sd myqtree -access-type allow -account NETAPPSIM\administrator -rights full-control -apply-to this-folder,sub-folders,files
|
||||
|
||||
#### create a task that adds security descriptor to the policy at a given path
|
||||
|
||||
vserver security file-directory policy task add -vserver netappsim-svm1 -policy-name myqtree -path /k_t3fp_b_cifs_r15/myqtree -ntfs-sd myqtree -ntfs-mode propagate -security-type ntfs
|
||||
|
||||
#### apply the policy
|
||||
|
||||
vserver security file-directory apply -vserver netappsim-svm1 -policy-name myqtree
|
||||
|
||||
#### delete the policy
|
||||
|
||||
vserver security file-directory policy delete myqtree
|
||||
|
||||
- It is safe to delete the policy, this will not effect the ACL's you
|
||||
have just applied to the qtree.
|
||||
|
||||
#### delete security descriptor rules
|
||||
|
||||
vserver security file-directory ntfs dacl remove -ntfs-sd myqtree -access-type *
|
||||
|
||||
- There is no need to clear the security descriptor rule when deleting
|
||||
the security descriptor.
|
||||
|
||||
#### delete security descriptor
|
||||
|
||||
vserver security file-directory ntfs delete -ntfs-sd myqtree
|
||||
|
||||
- It is safe to delete the security descriptor, this will not effect
|
||||
the ACL's you have just applied to the qtree.
|
||||
|
||||
#### check for effective permissions and leftover policy / security descriptor
|
||||
|
||||
vserver security file-directory show -vserver netappsim-svm1 -path /k_t3fp_b_cifs_r15/myqtree
|
||||
vserver security file-directory ntfs show
|
||||
vserver security file-directory policy show
|
||||
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
---
|
||||
- name: nested groups loop
|
||||
block:
|
||||
- name: Reset loop variables # without unsetting infinte loops will occur
|
||||
set_fact:
|
||||
group_present: false
|
||||
find_groups: []
|
||||
#find_users: [] # we dont want to reset this var, this will append on each loop until exit (unexpected behaviour but works)
|
||||
find_group_members_tidy: []
|
||||
group_members: []
|
||||
samaccountname_tidy: []
|
||||
samaccountname_group_members: []
|
||||
member_type: []
|
||||
member_attributes: []
|
||||
|
||||
# - name: Inspect dummy host object_attributes
|
||||
# debug:
|
||||
# msg: "{{ hostvars['DUMMY_HOST']['object_attributes'] }}"
|
||||
|
||||
- name: Filter groups into a list - CHANGED
|
||||
set_fact:
|
||||
find_groups: "{{ find_groups | default([]) + [dict(name=item.name, role=item.role, type=item.type)] }}"
|
||||
with_items: "{{ hostvars['DUMMY_HOST']['object_attributes'] }}"
|
||||
when: item.type == 'Group'
|
||||
|
||||
- name: Append users to list - CHANGED
|
||||
set_fact:
|
||||
find_users: "{{ find_users | default([]) + [dict(name=item.name, role=item.role, type=item.type)] }}"
|
||||
with_items: "{{ hostvars['DUMMY_HOST']['object_attributes'] }}"
|
||||
when: item.type == 'Person'
|
||||
|
||||
# - debug:
|
||||
# msg:
|
||||
# - "{{ find_groups }}"
|
||||
# - "{{ find_users }}"
|
||||
# when: find_groups is defined
|
||||
|
||||
- name: Query group members
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item.name }})").FindOne().Properties.member
|
||||
register: find_group_members_result
|
||||
with_items: "{{ find_groups }}"
|
||||
when: find_groups is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ find_group_members_result }}"
|
||||
# when: find_group_members_result is defined
|
||||
|
||||
- name: Tidy group members into dict of names and role
|
||||
set_fact:
|
||||
find_group_members_tidy: '{{ find_group_members_tidy | default([]) + [dict(name=item.name, role=item.role)] }}'
|
||||
with_items: "{{ find_group_members_result | json_query(jmesquery) }}"
|
||||
vars:
|
||||
jmesquery: "results[].{role: @.item.role, name: @.stdout_lines}"
|
||||
when: find_groups is defined # new - did i miss this in the demo
|
||||
|
||||
- name: Tidy group members into dict of name and role
|
||||
set_fact:
|
||||
group_members: "{{ group_members | default([]) + [dict(name=item.1.split(',')[0].split('CN=')[1], role=item.0.role)] }}"
|
||||
with_subelements:
|
||||
- "{{ find_group_members_tidy }}"
|
||||
- name
|
||||
when: find_groups is defined # new - did i miss this in the demo
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ group_members }}"
|
||||
# when: group_members is defined
|
||||
|
||||
# the UoN account field distinguishedname uses the short account name the same as samaccountname - this is helpful when looking up group members
|
||||
# distinguishedname {CN=tseed,CN=Users,DC=netappsim,DC=local}
|
||||
# samaccountname {tseed}
|
||||
#
|
||||
# out of the box AD will have the full name of the user in the distinguishedname field
|
||||
# distinguishedname {CN=Toby Seed,CN=Users,DC=netappsim,DC=local}
|
||||
# samaccountname {tseed}
|
||||
#
|
||||
# an additional check follows to lookup a samaccountname using the distinguishedname, this maintains compatibility with non UoN AD
|
||||
|
||||
- name: Find group members sAMAccountName
|
||||
win_shell: ([ADSISearcher] "(cn={{ item.name }})").FindOne().Properties.samaccountname
|
||||
register: samaccountname_result
|
||||
with_items: "{{ group_members }}"
|
||||
when: group_members is defined
|
||||
|
||||
- name: Tidy group members sAMAccountName into list
|
||||
set_fact:
|
||||
samaccountname_tidy: "{{ samaccountname_tidy | default([]) + [(item.stdout_lines)[0]] }}"
|
||||
with_items: "{{ samaccountname_result.results }}"
|
||||
when: group_members is defined
|
||||
|
||||
- name: Rebuild group_members dict with sAMAccountName
|
||||
set_fact:
|
||||
samaccountname_group_members: "{{ samaccountname_group_members | default([]) + [ dict(name=item[1], role=item[0].role) ] }}"
|
||||
loop: "{{ group_members|zip(samaccountname_tidy)|list }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ samaccountname_group_members }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: copy samaccountname_group_members to group_members
|
||||
set_fact:
|
||||
group_members: "{{ samaccountname_group_members }}"
|
||||
when: samaccountname_group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ group_members }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Check AD object is user or group
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item.name }})").FindOne().Properties.objectcategory
|
||||
register: member_type_result
|
||||
with_items: "{{ group_members }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ member_type_result }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Build list of AD object type
|
||||
set_fact:
|
||||
member_type: "{{ member_type | default([]) + [(item.stdout.split(',')[0].split('CN=')[1])] }}"
|
||||
with_items: "{{ member_type_result.results }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ member_type }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Build dict of object names and types
|
||||
set_fact:
|
||||
member_attributes: "{{ member_attributes | default([]) + [ dict(name=item[0].name, role=item[0].role, type=item[1]) ] }}" # effectively adding a new positional field from the list to the dict
|
||||
loop: "{{ group_members|zip(member_type)|list }}"
|
||||
when: group_members is defined
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ member_attributes }}"
|
||||
# when: group_members is defined
|
||||
|
||||
- name: Reset/Add variable object_attributes to dummy host for next loop
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
object_attributes: "{{ member_attributes }}"
|
||||
when: member_attributes is defined
|
||||
|
||||
- name: Set flag to notify there are nested groups
|
||||
set_fact:
|
||||
group_present: true
|
||||
with_items: "{{ member_attributes }}"
|
||||
when: member_attributes is defined and item.type == 'Group'
|
||||
#when: member_attributes is defined and item.type == 'GroupA' # used to break loop with test for no group_present
|
||||
|
||||
- name: Nested group present?
|
||||
fail:
|
||||
msg: Nested group detected, loop will run again
|
||||
when: group_present
|
||||
|
||||
# the following tasks only run on the last run of the loop where there are no more groups detected
|
||||
# append the users from the last run of the loop to find_users
|
||||
# set find_users as a DUMMY_HOST variable to be retrieved from the calling script - this is how you pass variables back from different plays also works for include_tasks
|
||||
|
||||
- name: Append users to list
|
||||
set_fact:
|
||||
find_users: "{{ find_users | default([]) + [dict(name=item.name, type=item.type, role=item.role)] }}"
|
||||
with_items: "{{ member_attributes }}"
|
||||
when: item.type == 'Person'
|
||||
|
||||
# if the script was passed the members parameter containing only a group and the requester_user_ad is a member of this group
|
||||
# there wont be an entry in the find_users dict as this wont have been caught and assigned the role='requestor' in the calling script
|
||||
# all requester flag logic could be moved here, the calling script wouldnt need to pass the role key, we keep it for illustration as
|
||||
# this script was origionally designed to assign roles for different RWX permissions on GPFS
|
||||
# we add the user to the dict with role='requester'
|
||||
- name: Check if requestor was nested in group and not passed in the members parameter
|
||||
set_fact:
|
||||
requester_found: "{{ item.name }}"
|
||||
with_items: "{{ find_users }}"
|
||||
when: item.name == requester_user_ad
|
||||
|
||||
- name: Add requestor if it was nested in group and not passed in the members parameter
|
||||
set_fact:
|
||||
find_users: "{{ find_users + [dict(name=requester_found, type='Person', role='requester')] }}"
|
||||
when: requester_found is defined
|
||||
|
||||
- name: Add users to variable find_users for dummy host on final loop
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
find_users: "{{ find_users }}"
|
||||
when: find_users is defined
|
||||
|
||||
rescue:
|
||||
|
||||
- include_tasks: group_lookup.yml
|
||||
|
|
@ -0,0 +1,782 @@
|
|||
---
|
||||
# PLAY
|
||||
# Gather cloudforms information and set service name
|
||||
- name: Query automate workspace
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- set_fact:
|
||||
endpoint: "{{ manageiq.api_url }}"
|
||||
auth_token: "{{ manageiq.api_token }}"
|
||||
request_id: "{{ (manageiq.request).split('/')[-1] }}"
|
||||
service_id: "{{ (manageiq.service).split('/')[-1] }}"
|
||||
user_id: "{{ (manageiq.user).split('/')[-1] }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# when users run ansible their manageiq auth token does not have sufficient rights to interact with the API, no combination of rights in a role for a non admin user are sufficient
|
||||
# use the local admin credentials to get an auth token
|
||||
- name: Get auth token
|
||||
uri:
|
||||
# ansible runner timeout, something changed from 5.11.1.2 to 5.11.6.0?
|
||||
#url: "{{ endpoint }}/api/auth"
|
||||
url: "https://127.0.0.1/api/auth"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
user: "{{ api_user }}"
|
||||
password: "{{ api_pass }}"
|
||||
status_code: 200
|
||||
register: login
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
auth_token: "{{ login.json.auth_token }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- name: Get requester user attributes
|
||||
uri:
|
||||
#url: "{{ endpoint }}/api/users/{{ user_id }}"
|
||||
url: "https://127.0.0.1/api/users/{{ user_id }}"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
status_code: 200
|
||||
register: user
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
requester_email: "{{ user.json.email }}"
|
||||
when: manageiq is defined and user.json.email is not none # cloudforms admin user has no email address (unless set), this is a null field in json
|
||||
|
||||
- set_fact:
|
||||
requester_email: "{{ from_email }}" # from_email should be able to recieve mail via relay
|
||||
when: requester_email is not defined
|
||||
|
||||
- set_fact:
|
||||
requester_user: "{{ user.json.name }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# when run from the command line set a default requester user
|
||||
- set_fact:
|
||||
requester_user: "command line invocation"
|
||||
when: manageiq is not defined
|
||||
|
||||
- name: define qtree name suffix, use the CloudForms request id as the qtree name suffix
|
||||
set_fact:
|
||||
qtree_suffix: "{{ request_id }}"
|
||||
when: manageiq is defined
|
||||
|
||||
- name: generate qtree name suffix, generate random string for qtree name suffix
|
||||
shell: head /dev/urandom | tr -dc A-Z0-9 | head -c 10 ; echo ''
|
||||
register: qtree_suffix
|
||||
when: manageiq is not defined
|
||||
|
||||
- name: define qtree name suffix
|
||||
set_fact:
|
||||
qtree_suffix: "{{ qtree_suffix.stdout }}"
|
||||
when: manageiq is not defined
|
||||
|
||||
# - name: DEBUG print all user attributes
|
||||
# debug:
|
||||
# msg:
|
||||
# - "{{ user }}"
|
||||
#
|
||||
# useful fields:
|
||||
# "email": "ucats@exmail.nottingham.ac.uk"
|
||||
# "name": "Toby Seed"
|
||||
# "userid": "toby.seed@nottingham.ac.uk"
|
||||
#
|
||||
# we see that the UON active directory schema has a different correlation between the AD short login id and the lookup of the userid and name
|
||||
# the above user generally would use the login id "ucats" across the UON estate to authenticate against AD
|
||||
# interestingly cloudforms queries several fields in the schema and will allow login as "ucats" and "toby.seed@nottingham.ac.uk"
|
||||
# to detect if this script is being run in self service mode, a match is performed against the requesting user and a single entry in groupmember list
|
||||
# we can only retrieve the expected AD short login id from the email field with what is passed by cloudforms when using UON AD
|
||||
|
||||
- name: get AD account name
|
||||
set_fact:
|
||||
requester_user_ad: "{{ (requester_email).split('@')[0] }}"
|
||||
#when: manageiq is defined
|
||||
|
||||
- name: get service
|
||||
uri:
|
||||
url: "https://127.0.0.1/api/services/{{ service_id }}"
|
||||
validate_certs: no
|
||||
method: GET
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
status_code: 200
|
||||
register: service
|
||||
when: manageiq is defined
|
||||
|
||||
- set_fact:
|
||||
service_name: "{{ service.json.name }}"
|
||||
new_service_name: "{{ service.json.name }} {{ request_id }}"
|
||||
when: manageiq is defined
|
||||
|
||||
# not using yet - will be used in emails
|
||||
# - set_fact:
|
||||
# service_name: "command line invocation"
|
||||
# new_service_name: "command line invocation"
|
||||
# when: manageiq is not defined
|
||||
|
||||
- name: set service name
|
||||
uri:
|
||||
url: "https://127.0.0.1/api/services/{{ service_id }}"
|
||||
validate_certs: no
|
||||
method: POST
|
||||
headers:
|
||||
X-Auth-Token: "{{ auth_token }}"
|
||||
body_format: json
|
||||
body: { "action" : "edit", "resource" : { "name" : "{{ new_service_name }}" }}
|
||||
status_code: 200, 204
|
||||
register: service
|
||||
when: manageiq is defined
|
||||
|
||||
# PLAY
|
||||
# Validate parameters passed to script from cloudforms
|
||||
- name: Script input Validation
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
vars:
|
||||
groupmemberslist: []
|
||||
groupmemberslistvalidate: []
|
||||
|
||||
tasks:
|
||||
|
||||
# REQUIREMENT
|
||||
# need a task to check for placeholder values here when parameterized for cloudforms
|
||||
|
||||
- name: Split groupmembers parameter on , delimiter
|
||||
set_fact:
|
||||
groupmemberslist: "{{ groupmemberslist }} + [ '{{ item }}' ]"
|
||||
with_items: "{{ members.split(',') }}"
|
||||
|
||||
- name: Remove empty fields from groupmembers parameter
|
||||
set_fact:
|
||||
groupmemberslistvalidate: "{{ groupmemberslistvalidate }} + [ '{{ item }}' ]"
|
||||
when: item | length != 0
|
||||
with_items: "{{ groupmemberslist }}"
|
||||
|
||||
- name: Remove duplicate entries
|
||||
set_fact:
|
||||
groupmemberslistvalidate: "{{ groupmemberslistvalidate | unique }}"
|
||||
|
||||
# PLAY
|
||||
# Add winrm host used for AD querys to inventory
|
||||
- name: Build inventory for AD server
|
||||
hosts: localhost
|
||||
gather_facts: False
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Add host entry for adserver
|
||||
add_host: >
|
||||
name=adserver
|
||||
groups=windows
|
||||
ansible_host="{{ ad_host }}"
|
||||
|
||||
# PLAY
|
||||
# Query winrm to validate AD user/group exist, build dict for share ACL and dict of all user/email from a recursively search of user/group
|
||||
- name: Check AD user exists
|
||||
hosts: adserver
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
vars:
|
||||
groupmemberslistvalidate: "{{ hostvars['localhost']['groupmemberslistvalidate'] }}"
|
||||
requester_user_ad: "{{ hostvars['localhost']['requester_user_ad'] }}"
|
||||
no_aduser: []
|
||||
object_type: []
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Add connectivity variables for adserver
|
||||
set_fact:
|
||||
ansible_user: "{{ ad_user }}"
|
||||
ansible_password: "{{ ad_pass }}"
|
||||
ansible_connection: "{{ ad_connection }}"
|
||||
ansible_winrm_transport: "{{ ad_winrm_transport }}"
|
||||
ansible_winrm_kinit_mode: "{{ ad_winrm_kinit_mode }}"
|
||||
ansible_winrm_message_encryption: "{{ ad_winrm_message_encryption }}"
|
||||
ansible_port: "{{ ad_port }}"
|
||||
ansible_winrm_scheme: "{{ ad_winrm_scheme }}"
|
||||
ansible_winrm_server_cert_validation: "{{ ad_winrm_server_cert_validation }}"
|
||||
# ansible_winrm_operation_timeout_sec: 60
|
||||
# ansible_winrm_read_timeout_sec: 60
|
||||
|
||||
- name: Check AD user/group exists
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne()
|
||||
register: command_result
|
||||
with_items:
|
||||
- "{{ groupmemberslistvalidate }}"
|
||||
|
||||
- name: Flag fail where AD user/group not exist
|
||||
set_fact:
|
||||
no_aduser: "{{ no_aduser + [item.item] }}"
|
||||
when: item.stdout | length == 0
|
||||
with_items: "{{ command_result.results }}"
|
||||
changed_when: true
|
||||
#notify: topic_noad # would be used for handler for failure conditions by console and email
|
||||
|
||||
- name: Check AD object is user or group
|
||||
win_shell: ([ADSISearcher] "(sAMAccountName={{ item.item }})").FindOne().Properties.objectcategory
|
||||
register: object_result
|
||||
with_items: "{{ command_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Build list of AD object type
|
||||
set_fact:
|
||||
object_type: "{{ object_type + [(item.stdout.split(',')[0].split('CN=')[1])] }}" # faster than regex
|
||||
with_items: "{{ object_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# if the cloudforms requester's AD account requester_user_ad is in the list of users, set the role requester, this will be used for self service emails
|
||||
- name: Build dict of object names, types and roles positionally from list with object name and list with object type
|
||||
set_fact:
|
||||
object_attributes: "{{ object_attributes | default([]) + [dict(name=item[0], type=item[1], role='requester' if (item[0] == requester_user_ad) else 'member') ] }}"
|
||||
loop: "{{ groupmemberslistvalidate|zip(object_type)|list }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Register dummy host with variable object_attributes
|
||||
add_host:
|
||||
name: "DUMMY_HOST"
|
||||
object_attributes: "{{ object_attributes }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Find all group members
|
||||
include_tasks: group_lookup.yml
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - name: Inspect all users who will require email notification
|
||||
# debug:
|
||||
# msg: "{{ hostvars['DUMMY_HOST']['find_users'] }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
- name: Import dummy host variable from group_lookup.yml
|
||||
set_fact:
|
||||
unique_users: "{{ hostvars['DUMMY_HOST']['find_users'] }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- name: Get unique name/role/type entries, remove duplicate entries that may arise from group nesting
|
||||
set_fact:
|
||||
unique_users: "{{ unique_users | unique }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ unique_users }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
- name: Get requester name into a list
|
||||
set_fact:
|
||||
requester_users: "{{ requester_users | default([]) + [item.name] }}"
|
||||
with_items: "{{ unique_users }}"
|
||||
when: no_aduser | length == 0 and item.role == 'requester'
|
||||
|
||||
- name: Get member names into a list
|
||||
set_fact:
|
||||
member_users: "{{ member_users | default([]) + [item.name] }}"
|
||||
with_items: "{{ unique_users }}"
|
||||
when: no_aduser | length == 0 and item.role == 'member'
|
||||
|
||||
- name: Get names common in both lists
|
||||
set_fact:
|
||||
common_users: "{{ requester_users | intersect(member_users) }}"
|
||||
when: no_aduser | length == 0 and requester_users is defined
|
||||
|
||||
- name: Remove member entries where competing requester entry exists
|
||||
set_fact:
|
||||
email_users: "{{ email_users | default([]) + [dict(name=item.name, role=item.role, type=item.type)] }}"
|
||||
with_items: "{{ unique_users }}"
|
||||
#when: no_aduser | length == 0 and (item.name not in common_users or (item.name in common_users and item.role == 'requester'))
|
||||
when: no_aduser | length == 0 and (requester_users is defined and (item.name not in common_users or (item.name in common_users and item.role == 'requester')))
|
||||
|
||||
- name: Set email_users where requester not present in the user list
|
||||
set_fact:
|
||||
email_users: "{{ unique_users }}"
|
||||
when: no_aduser | length == 0 and requester_users is not defined
|
||||
|
||||
# deduplicated dict to send appropriate class of email to users
|
||||
# - debug:
|
||||
# msg: "{{ email_users }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
- name: Get member email address from AD
|
||||
win_shell: Get-ADUser {{ item.name }} -Properties mail | Select-Object -ExpandProperty mail
|
||||
register: email_result
|
||||
with_items: "{{ email_users }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ email_result }}"
|
||||
# when: no_aduser | length == 0
|
||||
|
||||
# this would crash out where customer account has no associated email, UoN are very consistent with account creation and adding email
|
||||
# check for an empty email and substitute for 'none', this dict key will be evaluated when sending emails
|
||||
- name: Get member emails into a list
|
||||
set_fact:
|
||||
email_address: "{{ email_address | default([]) + [item.stdout_lines[0] if (item.stdout_lines | length > 0) else 'none' ] }}"
|
||||
with_items: "{{ email_result.results }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# - debug:
|
||||
# msg: "{{ email_address }}"
|
||||
|
||||
- name: Build dict of object name, role, type and email
|
||||
set_fact:
|
||||
user_dict: "{{ user_dict | default([]) + [dict(name=item[0].name, role=item[0].role, type=item[0].type, email=item[1])] }}" # adding a new positional field from the list to the dict
|
||||
loop: "{{ email_users|zip(email_address)|list }}"
|
||||
when: no_aduser | length == 0
|
||||
|
||||
- debug:
|
||||
msg:
|
||||
- "{{ object_attributes }}" # use for the ACL's
|
||||
- "{{ user_dict }}" # use to identify users that will get various classes of email
|
||||
when: no_aduser | length == 0
|
||||
|
||||
# REQUIREMENT
|
||||
# drop in handlers for noad failure here
|
||||
|
||||
# PLAY
|
||||
# Create qtree / quota / dacl and define windows host
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
name: Create/Delete qtree and quota
|
||||
vars:
|
||||
#state: "{{ 'present' if perform == 'create' else ( 'absent' if perform == 'delete' else 'placeholder') }}" # no remove logic yet
|
||||
qtree_suffix: "{{ hostvars['localhost']['qtree_suffix'] }}"
|
||||
ADPSuser: "{{ domain }}\\{{ ad_user }}"
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
- vars/requests.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- set_fact:
|
||||
human_to_byte_string: "{{ qtree_quota }} {{ qtree_quota_unit }}"
|
||||
|
||||
- set_fact:
|
||||
quota_hard_limit: "{{ human_to_byte_string|human_to_bytes}}"
|
||||
quota_soft_limit: "{{( human_to_byte_string|human_to_bytes | float / 100 * qtree_quota_soft_limit) | round | int | abs }}"
|
||||
|
||||
# when run from cloudforms we would pass the prefix/suffix from the dialog or set from the retrieved request ID
|
||||
- name: generate qtree name suffix, generate random string for qtree name suffix
|
||||
shell: head /dev/urandom | tr -dc A-Z0-9 | head -c 10 ; echo ''
|
||||
register: qtree_suffix
|
||||
|
||||
- name: define qtree name suffix
|
||||
set_fact:
|
||||
qtree_suffix: "{{ qtree_suffix.stdout }}"
|
||||
|
||||
- set_fact:
|
||||
qtree_name: "{{ qtree_prefix }}_{{ qtree_suffix }}"
|
||||
|
||||
# REQUIREMENT
|
||||
# volume space reporting, actual space vs over provisioned space + accociated failure consition and console/email reporting via handler
|
||||
|
||||
# REQUIREMENT
|
||||
# check qtree name doesnt already exist and hard exit (for cloudforms failed request) with a requester email
|
||||
|
||||
# REQUIREMENT
|
||||
# all API requests need their own include tasks and status_code evaluations with handler topics
|
||||
# far too much repetition of queued job checking
|
||||
|
||||
- name: Get svm UUID
|
||||
uri:
|
||||
url: "{{ getSvmEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
svm_uuid: "{{ (response.json.records | json_query(jmesquery))[0] }}"
|
||||
vars:
|
||||
jmesquery: "[?name == '{{ netapp_svm_name }}'].uuid"
|
||||
|
||||
- name: Get volume UUID
|
||||
uri:
|
||||
url: "{{ getVolumesEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
volume_uuid: "{{ (response.json.records | json_query(jmesquery))[0] }}"
|
||||
vars:
|
||||
jmesquery: "[?name == '{{ volume_name }}'].uuid"
|
||||
|
||||
- name: Create qtree
|
||||
uri:
|
||||
url: "{{ postQtreeEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: POST
|
||||
body_format: json
|
||||
body: "{{ postQtree }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 202
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
job_uuid: "{{ response.json.job.uuid }}"
|
||||
|
||||
- name: Get job status
|
||||
uri:
|
||||
url: "{{ getJobEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
register: check_response
|
||||
until: check_response.json.state != 'running'
|
||||
retries: "{{ api_retry }}"
|
||||
delay: 10
|
||||
|
||||
- name: Report job failure
|
||||
fail:
|
||||
msg:
|
||||
- "{{ check_response.json.message }}"
|
||||
when: check_response.json.state == 'failure'
|
||||
|
||||
# not required
|
||||
# - name: Get qtree ID
|
||||
# uri:
|
||||
# url: "{{ getQtreeEndpoint }}"
|
||||
# user: "{{ netapp_svm_user }}"
|
||||
# password: "{{ netapp_svm_pass }}"
|
||||
# method: GET
|
||||
# validate_certs: no
|
||||
# return_content: yes
|
||||
# status_code: 200
|
||||
# register: response
|
||||
|
||||
# - set_fact:
|
||||
# qtree_id: "{{ (response.json.records | json_query(jmesquery))[0] }}"
|
||||
# vars:
|
||||
# jmesquery: "[?name == '{{ qtree_name }}'].id"
|
||||
|
||||
- name: Create quota
|
||||
uri:
|
||||
url: "{{ postQuotaEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: POST
|
||||
body_format: json
|
||||
body: "{{ postQuota }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 202
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
job_uuid: "{{ response.json.job.uuid }}"
|
||||
|
||||
- name: Get job status
|
||||
uri:
|
||||
url: "{{ getJobEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
register: check_response
|
||||
until: check_response.json.state != 'running'
|
||||
retries: "{{ api_retry }}"
|
||||
delay: 10
|
||||
|
||||
- name: Report job failure
|
||||
fail:
|
||||
msg:
|
||||
- "{{ check_response.json.message }}"
|
||||
when: check_response.json.state == 'failure'
|
||||
|
||||
- set_fact:
|
||||
toggle_quota: "false"
|
||||
|
||||
- name: Toggle volume quota off
|
||||
uri:
|
||||
url: "{{ toggleVolQuotaEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: PATCH
|
||||
body_format: json
|
||||
body: "{{ toggleVolQuota }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 202
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
job_uuid: "{{ response.json.job.uuid }}"
|
||||
|
||||
- name: Get job status
|
||||
uri:
|
||||
url: "{{ getJobEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
register: response
|
||||
until: response.json.state != 'running'
|
||||
retries: "{{ api_retry }}"
|
||||
delay: 10
|
||||
|
||||
- name: Report job failure
|
||||
fail:
|
||||
msg:
|
||||
- "{{ response.json.message }}"
|
||||
when: response.json.state == 'failure'
|
||||
|
||||
# the API will report a quota job finished but blocks another quota command, requires a brief pause on a busy system
|
||||
- pause:
|
||||
seconds: "{{ netapp_cli_sleep }}"
|
||||
|
||||
- set_fact:
|
||||
toggle_quota: "true"
|
||||
|
||||
- name: Toggle volume quota on
|
||||
uri:
|
||||
url: "{{ toggleVolQuotaEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: PATCH
|
||||
body_format: json
|
||||
body: "{{ toggleVolQuota }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 202
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
job_uuid: "{{ response.json.job.uuid }}"
|
||||
|
||||
# enabling quota will take some time, ensure the retries cover this with larger filesystems
|
||||
- name: Get job status
|
||||
uri:
|
||||
url: "{{ getJobEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
register: response
|
||||
until: response.json.state != 'running'
|
||||
retries: "{{ api_retry }}"
|
||||
delay: 10
|
||||
|
||||
- name: Report job failure
|
||||
fail:
|
||||
msg:
|
||||
- "{{ response.json.message }}"
|
||||
when: response.json.state == 'failure'
|
||||
|
||||
# these commented tasks are included to show the ansible native ontap cli module, the play moved to using the SVM instead of the ClusterManager on request of the storage team, necessitating API calls rather than the ontap modules
|
||||
# if the ClusterManager target is reinstated for this play, either modify the CLI API endpoints (preferable due to job control) or use this module and be mindful of sleep command
|
||||
#
|
||||
# there is no simple one liner for the cli to wait for the job to finish, there is a sleep command to mitigate but may need to be tuned on a busy system
|
||||
# - name: Apply DACL for winrm powershell user account
|
||||
# na_ontap_command:
|
||||
# command:
|
||||
# - 'vserver security file-directory policy create -vserver {{ netapp_vserver_name }} -policy-name {{ qtree_name }};'
|
||||
# - 'vserver security file-directory ntfs dacl add -vserver {{ netapp_vserver_name }} -ntfs-sd {{ qtree_name }} -access-type allow -account {{ ADPSuser }} -rights full-control -apply-to this-folder,sub-folders,files;'
|
||||
# - 'vserver security file-directory policy task add -vserver {{ netapp_vserver_name }} -policy-name {{ qtree_name }} -path /{{ volume_name }}/{{ qtree_name }} -ntfs-sd {{ qtree_name }} -ntfs-mode propagate -security-type ntfs;'
|
||||
# - 'vserver security file-directory apply -vserver {{ netapp_vserver_name }} -policy-name {{ qtree_name }};'
|
||||
# - 'echo about to remove policy {{ qtree_name }} and security descriptor {{ qtree_name }};'
|
||||
# - 'sleep {{ netapp_cli_sleep }};'
|
||||
# - 'vserver security file-directory policy delete {{ qtree_name }};'
|
||||
# - 'vserver security file-directory ntfs delete -ntfs-sd {{ qtree_name }}'
|
||||
# privilege: 'admin'
|
||||
# return_dict: false # fails with compound commands when true
|
||||
# https: true
|
||||
# validate_certs: false
|
||||
# use_rest: Always
|
||||
# hostname: "{{ netapp_hostname }}"
|
||||
# username: "{{ netapp_username }}"
|
||||
# password: "{{ netapp_password }}"
|
||||
# ignore_errors: True
|
||||
# register: ontapCmd
|
||||
|
||||
# - debug:
|
||||
# msg:
|
||||
# - "DACL application failed, try increasing the timeout in the command list"
|
||||
# - "{{ ontapCmd.msg }}"
|
||||
# when: ontapCmd.failed
|
||||
|
||||
- name: Create DACL policy
|
||||
uri:
|
||||
url: "{{ postDACLPolicyEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: POST
|
||||
body_format: json
|
||||
body: "{{ postDACLPolicy }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 201
|
||||
register: response
|
||||
|
||||
- name: Create DACL policy attributes
|
||||
uri:
|
||||
url: "{{ postDACLPolicyAttributesEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: POST
|
||||
body_format: json
|
||||
body: "{{ postDACLPolicyAttributes }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
- name: Create DACL policy target
|
||||
uri:
|
||||
url: "{{ postDACLPolicyTargetEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: POST
|
||||
body_format: json
|
||||
body: "{{ postDACLPolicyTarget }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
- name: Apply DACL policy
|
||||
uri:
|
||||
url: "{{ postDACLPolicyApplyEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: POST
|
||||
body_format: json
|
||||
body: "{{ postDACLPolicyApply }}"
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
- set_fact:
|
||||
job_uuid: "{{ response.json.job.uuid }}"
|
||||
|
||||
- name: Get job status
|
||||
uri:
|
||||
url: "{{ getJobEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
register: check_response
|
||||
until: check_response.json.state != 'running'
|
||||
retries: "{{ api_retry }}"
|
||||
delay: 10
|
||||
|
||||
- name: Report job failure
|
||||
fail:
|
||||
msg:
|
||||
- "{{ check_response.json.message }}"
|
||||
when: check_response.json.state == 'failure'
|
||||
|
||||
- name: Delete DACL policy
|
||||
uri:
|
||||
url: "{{ deleteDACLPolicyEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: DELETE
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
- name: Delete DACL security descriptor
|
||||
uri:
|
||||
url: "{{ deleteDACLPolicyAttributesEndpoint }}"
|
||||
user: "{{ netapp_svm_user }}"
|
||||
password: "{{ netapp_svm_pass }}"
|
||||
method: DELETE
|
||||
validate_certs: no
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: response
|
||||
|
||||
# REQUIREMENT
|
||||
# handlers needed for this play, console and email failure conditions
|
||||
|
||||
# PLAY
|
||||
# Set windows host connection parameters and run powershell over winrm
|
||||
- hosts: adserver
|
||||
gather_facts: false
|
||||
become_method: runas
|
||||
name: Change windows ACL for qtree
|
||||
vars:
|
||||
qtree_name: "{{ hostvars['localhost']['qtree_name'] }}"
|
||||
object_attributes: "{{ hostvars['localhost']['object_attributes'] }}"
|
||||
vars_files:
|
||||
vars/main.yml
|
||||
|
||||
tasks:
|
||||
|
||||
# to avoid using group_vars we set_facts that were not accepted by add_host, add_host does not work with many windows winrm connectivity vars
|
||||
- name: Add connectivity variables for adserver
|
||||
set_fact:
|
||||
ansible_user: "{{ ad_user }}"
|
||||
ansible_password: "{{ ad_pass }}"
|
||||
ansible_connection: "{{ ad_connection }}"
|
||||
ansible_winrm_transport: "{{ ad_winrm_transport }}"
|
||||
ansible_winrm_kinit_mode: "{{ ad_winrm_kinit_mode }}"
|
||||
ansible_winrm_message_encryption: "{{ ad_winrm_message_encryption }}"
|
||||
ansible_port: "{{ ad_port }}"
|
||||
ansible_winrm_scheme: "{{ ad_winrm_scheme }}"
|
||||
ansible_winrm_server_cert_validation: "{{ ad_winrm_server_cert_validation }}"
|
||||
#ansible_winrm_operation_timeout_sec: 60
|
||||
#ansible_winrm_read_timeout_sec: 60
|
||||
|
||||
- name: Copy powershell script to winrm host
|
||||
win_template:
|
||||
src: templates/ps_acl.ps1.j2
|
||||
dest: "{{ temp_dir }}{{ qtree_name }}.ps1"
|
||||
|
||||
# remove everyone permission set users/group permission, cannot be achieved with DACL
|
||||
- name: Apply ACL to share
|
||||
#win_command: powershell.exe -ExecutionPolicy Unrestricted {{ qtree_name }}.ps1 # powershell permission model is awkward over win_shell and win_command
|
||||
win_command: powershell.exe -ExecutionPolicy ByPass -File {{ temp_dir }}{{ qtree_name }}.ps1
|
||||
become: yes
|
||||
become_user: Administrator # service_cloudforms may need local admin permissions or some winrm permission elevation in a prod environment
|
||||
register: command_result
|
||||
|
||||
- debug:
|
||||
msg: "{{ command_result }}"
|
||||
|
||||
- name: Remove powershell script from winrm host
|
||||
win_file:
|
||||
path: "{{ temp_dir }}{{ qtree_name }}.ps1"
|
||||
state: absent
|
||||
|
||||
- debug:
|
||||
msg:
|
||||
- "//{{netapp_svm_host}}/{{volume_name}}/{{ qtree_name }}"
|
||||
|
||||
# REQUIREMENT
|
||||
# new play for reporting
|
||||
# handlers that evaluate state vars needed for this play, console and email failure conditions and (templated) provisioned emails to all users
|
||||
# will need logic for self service mode from the requester key in the appropriate dict
|
||||
|
||||
# REQUIREMENT?
|
||||
# there is no logic for delete/un-provision, playbook needs breaking out to include tasks that are conditionally run based on perform parameter
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#define share
|
||||
$share = "\\{{ netapp_svm_host }}\{{ volume_name }}\{{ qtree_name }}"
|
||||
|
||||
#block inheritance from parent netapp volume and remove inherited permissions
|
||||
$acl = Get-Acl $share
|
||||
$acl.SetAccessRuleProtection($true,$false)
|
||||
$acl | Set-Acl $share
|
||||
|
||||
#set ownership for netapp account BUILTIN\Administrators
|
||||
$acl = Get-Acl $share
|
||||
$object = New-Object System.Security.Principal.Ntaccount("BUILTIN\Administrators")
|
||||
$acl.SetOwner($object)
|
||||
$acl | Set-Acl $share
|
||||
|
||||
#set permissions for members with inheritance for subfolders and files
|
||||
{% for item in object_attributes %}
|
||||
$acl = Get-Acl $share
|
||||
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("{{ domain }}\{{ item.name }}","ExecuteFile, ReadData,ReadAttributes,ReadExtendedAttributes,Createfiles,AppendData,WriteAttributes,WriteExtendedAttributes,DeleteSubdirectoriesAndFiles,Delete,ReadPermissions","ContainerInherit, ObjectInherit","InheritOnly","Allow")
|
||||
$acl.SetAccessRule($AccessRule)
|
||||
$acl | Set-Acl $share
|
||||
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
# mandatory parameters for actions
|
||||
perform: create # should be "create" or "delete" not yet used in playbook
|
||||
members: tseed,swright,project,architect # users or groups to be applied to share ACL, this is a comma separated list
|
||||
#members: ,,OCF-ADM,OCF-ADM,tseed,,,,tseed # used to check input validation, dedupe and requester logic
|
||||
#members: ucats,ucasw2,uizrs,bhzajd,ui-cloudforms-dev # used to check UoN AD and nested-nested-nestedN group lookup
|
||||
|
||||
# email parameters
|
||||
smtp_relay: smtp.nottingham.ac.uk
|
||||
smtp_port: 25
|
||||
from_email: ucats@exmail.nottingham.ac.uk # should be a service account address such as donotreply@nottingham.ac.uk
|
||||
|
||||
# cloudforms API
|
||||
api_user: placeholder
|
||||
api_pass: placeholder
|
||||
|
||||
# windows host parameters that run powershell against qtree to set ACL
|
||||
#
|
||||
# variables to create in-memory inventory of the AD server, notice the ad_ variables are in the winrm format that would be under the entry [<hostgroup>:vars] for an inventory file
|
||||
#
|
||||
ad_host: WIN-1JE0R5GCBSG.NETAPPSIM.LOCAL # active directory server, this must be a fqdn (system-wide kerberos must be working with requisite krb5.conf + resolv.conf entries/tickets to find ad.nottingham.ac.uk)
|
||||
ad_user: administrator # AD service account capable of manipulating group membership and run powershell ACL against share
|
||||
ad_pass: "Password0" # AD service account password
|
||||
domain: NETAPPSIM # domain used in DACL
|
||||
ad_connection: winrm
|
||||
ad_winrm_transport: kerberos
|
||||
ad_winrm_kinit_mode: managed # allow ansible to manage own kerberos token, SSSD manages when set to manual
|
||||
ad_winrm_message_encryption: auto # can be set to always, depends on ad server profile
|
||||
ad_port: 5986 # 5985/http for non https transport, UON on-prem use 5986/https
|
||||
ad_winrm_scheme: https # UON on-prem use 5986/https
|
||||
ad_winrm_server_cert_validation: ignore
|
||||
temp_dir: C:\Windows\Temp\ # temporary location for powershell script
|
||||
#
|
||||
#ad_host: uiwdcjub04.ad.nottingham.ac.uk
|
||||
#ad_user: "service_CloudForms"
|
||||
#ad_pass: "As109pHY4Wi9o7naZnhr#!"
|
||||
#domain: AD
|
||||
#
|
||||
|
||||
# netapp svm connection parameters
|
||||
netapp_svm_host: 192.168.101.132 #fqdn or ip, UoN test svm not in dns netappsim-svm1
|
||||
netapp_svm_name: netappsim-svm1 #svm instance name, used in API
|
||||
netapp_svm_user: vsadmin #svm user
|
||||
netapp_svm_pass: Password0 #svm password
|
||||
volume_name: "k_t3fp_b_cifs_r15" #volume where qtree is to be created
|
||||
#
|
||||
# netapp_svm_host: 10.159.144.130
|
||||
# netapp_svm_name: UIDFSNET01_SVM999
|
||||
# netapp_svm_user: "ad\service_CloudForms"
|
||||
# netapp_svm_pass: "As109pHY4Wi9o7naZnhr#!"
|
||||
# volume_name: "TESTFlexgroup"
|
||||
#
|
||||
|
||||
# qtree and quota settings
|
||||
qtree_prefix: "CF" # qtree prefix, CF_12345
|
||||
qtree_quota: 2 # size
|
||||
qtree_quota_unit: GB # MB, GB, TB, PB
|
||||
qtree_quota_soft_limit: 70 # % of qtree_quota
|
||||
|
||||
# tuning API calls
|
||||
api_retry: 10 # number of retries
|
||||
netapp_cli_sleep: 5 # seconds wait
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# https://<cluster_mgmt_ip_address>/docs/api
|
||||
getSvmEndpoint: "https://{{ netapp_svm_host }}/api/svm/svms"
|
||||
getVolumesEndpoint: "https://{{ netapp_svm_host }}/api/storage/volumes"
|
||||
postQtreeEndpoint: "https://{{ netapp_svm_host }}/api/storage/qtrees"
|
||||
getJobEndpoint: "https://{{ netapp_svm_host }}/api/cluster/jobs/{{ job_uuid }}"
|
||||
getQtreeEndpoint: "https://{{ netapp_svm_host }}/api/storage/qtrees"
|
||||
postQuotaEndpoint: "https://{{ netapp_svm_host }}/api/storage/quota/rules"
|
||||
toggleVolQuotaEndpoint: "https://{{ netapp_svm_host }}/api/storage/volumes/{{ volume_uuid }}"
|
||||
postDACLPolicyEndpoint: "https://{{ netapp_svm_host }}/api/private/cli/vserver/security/file-directory/policy"
|
||||
postDACLPolicyAttributesEndpoint: "https://{{ netapp_svm_host }}/api/private/cli/vserver/security/file-directory/ntfs/dacl/add"
|
||||
postDACLPolicyTargetEndpoint: "https://{{ netapp_svm_host }}/api/private/cli/vserver/security/file-directory/policy/task/add"
|
||||
postDACLPolicyApplyEndpoint: "https://{{ netapp_svm_host }}/api/private/cli/vserver/security/file-directory/apply"
|
||||
deleteDACLPolicyEndpoint: "https://{{ netapp_svm_host }}/api/private/cli/vserver/security/file-directory/policy?policy_name={{ qtree_name }}"
|
||||
deleteDACLPolicyAttributesEndpoint: "https://{{ netapp_svm_host }}/api/private/cli/vserver/security/file-directory/ntfs?ntfs_sd={{ qtree_name }}"
|
||||
postQtree: {
|
||||
"name": "{{ qtree_name }}",
|
||||
"security_style": "ntfs",
|
||||
"svm": {
|
||||
"name": "{{ netapp_svm_name }}",
|
||||
"uuid": "{{ svm_uuid }}"
|
||||
},
|
||||
"volume": {
|
||||
"name": "{{ volume_name }}",
|
||||
"uuid": "{{ volume_uuid }}"
|
||||
}
|
||||
}
|
||||
toggleVolQuota: {
|
||||
"quota": {
|
||||
"enabled": "{{ toggle_quota }}"
|
||||
}
|
||||
}
|
||||
postQuota: {
|
||||
"qtree": {
|
||||
"name": "{{ qtree_name }}"
|
||||
},
|
||||
"space": {
|
||||
"hard_limit": "{{ quota_hard_limit}}",
|
||||
"soft_limit": "{{ quota_soft_limit }}"
|
||||
},
|
||||
"svm": {
|
||||
"name": "{{ netapp_svm_name }}"
|
||||
},
|
||||
"type": "tree",
|
||||
"volume": {
|
||||
"name": "{{ volume_name }}"
|
||||
}
|
||||
}
|
||||
postDACLPolicy: {
|
||||
"policy-name" : "{{ qtree_name }}"
|
||||
}
|
||||
postDACLPolicyAttributes: {
|
||||
"ntfs-sd": "{{ qtree_name }}",
|
||||
"access-type": "allow",
|
||||
"account": "{{ ADPSuser }}",
|
||||
"rights": "full-control",
|
||||
"apply-to": ["this-folder", "sub-folders", "files"]
|
||||
}
|
||||
postDACLPolicyTarget: {
|
||||
"policy-name": "{{ qtree_name }}",
|
||||
"path": "/{{ volume_name }}/{{ qtree_name }}",
|
||||
"ntfs-sd": ["{{ qtree_name }}"],
|
||||
"ntfs-mode": "propagate",
|
||||
"security-type": "ntfs"
|
||||
}
|
||||
postDACLPolicyApply: {
|
||||
"policy-name": "{{ qtree_name }}"
|
||||
}
|
||||
Loading…
Reference in New Issue