Skip to content

Commit 8d331cf

Browse files
authored
Merge pull request #62 from QualiSystemsLab/feature/dan-aws_tf_backend
Feature/dan aws tf backend
2 parents 06ed6fb + 827c11c commit 8d331cf

File tree

25 files changed

+1722
-41
lines changed

25 files changed

+1722
-41
lines changed

.gitignore

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ docs/_build/
6161
# PyBuilder
6262
target/
6363
cloudshell_config.yml
64-
shells/generic_terraform_service/tests/.env
65-
tests/.env
66-
shells/backends/azure_tf_backend/tests/.env
64+
65+
# env files
66+
*.env
67+
!*.template.env

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Additional workflow recommendation: it is very easy to customize a Blueprint set
2828
|Github Terraform Module URL|String|Path to target module. Can be provided in three formats: <br/> 1) https://github.com/{ACCOUNT}/{REPO}/tree/{BRANCH}/{PATH_TO_FOLDER} <br/> 2) https://github.com/{ACCOUNT}/{REPO}/blob/{BRANCH}/{PATH_TO_FOLDER}/{FILENAME}.tf <br/> 3) https://raw.githubusercontent.com/{ACCOUNT}/{REPO}/{BRANCH}/{PATH_TO_FOLDER}/{FILENAME}.tf | Yes |
2929
|Terraform Version|String|The version of terraform.exe that will be downloaded and used (If not specified latest version will be used)| No |
3030
|Github Token|String| Github PAT (Private Access Token) to be used in order to download TF module. The entire repo will be downloaded and then the referenced TF module will be executed | Yes |
31-
|Cloud Provider|String| Reference to the CloudProvider resource that should be used to initialize the Terrafom provider. Supported cloud providers: <br> - Azure Shell <br>- Azure Shell 2G| Yes |
31+
|Cloud Provider|String| Reference to the CloudProvider resource that should be used to initialize the Terrafom provider. Supported cloud providers: <br> - Azure Shell <br>- Azure Shell 2G <br> - AWS Shell <br> - AWS Shell 2G| Yes |
3232
|Branch|String| In case specified will override the branch in the Github Terraform Module URL | No |
3333
|Terraform Outputs|String| Unmapped and *non-sensitive* TF outputs will be stored as a CSV list of key value pairs on this attribute. This attribute is optional. | No |
3434
|Terraform Sensitive Outputs|Password| Unmapped and *sensitive* TF outputs will be stored as a CSV list of key value pairs on this attribute. This attribute must be of type "password. The attribute is optional. | No |
@@ -92,7 +92,17 @@ Cloud Provider|String| Cloud Provider resource name that holds the authenticatio
9292
Resource Group|String| The resource group of the Storage Account|
9393

9494
### AWS Remote Provider Shell (backends\aws_tf_backend)
95-
Coming soon
95+
The AWS Remote Provider shell is used in order to enable CloudShell access to AWS S3 storage, to then be used to store the remote state file.</br>
96+
One must create a resource and fill in the attributes - then specify that resource name as the Remote State Provider.
97+
Only one type of authentication is allowed, either by Access Key+Secret Key or using the Cloud Provider authentication keys. If both options are specified it will throw an error, so please supply only 1 option.
98+
99+
|Attribute|Type|Description|
100+
|:-----|:-----|:-----|
101+
Bucket Name|String| The name of the Bucket to be used |
102+
Region Name|String| The Region to be used with AWS |
103+
Access Key|String| Access Key of AWS Account|
104+
Secret Key|String| Secret Key of AWS Account|
105+
Cloud Provider|String| Cloud Provider resource name that holds the authentication keys in case not filling the Keys|
96106

97107
\* Additional Remote Backend Providers are coming soon
98108

deploy.bat

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ xcopy /Q /Y . "C:\Program Files (x86)\QualiSystems\CloudShell\Server\Config\Pypi
88
cd ..\..
99
cd shells\backends\azure_tf_backend
1010
shellfoundry install
11-
cd ..\..\..
11+
cd ..\..
12+
cd backends\aws_tf_backend
13+
shellfoundry install
14+
cd ..\..\..

package/cloudshell/iac/terraform/constants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@
3636
TF_WORKING_DIR = "TF_WORKING_DIR"
3737

3838
# CLP models
39+
AZURE1G_MODEL = "Microsoft Azure"
40+
AWS1G_MODEL = "AWS EC2"
41+
3942
AZURE2G_MODEL = "Microsoft Azure Cloud Provider 2G"
43+
AWS2G_MODEL = "Amazon AWS Cloud Provider 2G"
44+
45+
CLP_PROVIDER_MODELS = [AWS1G_MODEL, AWS2G_MODEL, AZURE1G_MODEL, AZURE2G_MODEL]
4046

4147
# Misc
4248
DIRTY_CHARS = r'''
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import os
2+
from abc import ABCMeta
3+
4+
from cloudshell.iac.terraform.models.shell_helper import ShellHelperObject
5+
6+
7+
class BaseCloudProviderEnvVarHandler(metaclass=ABCMeta):
8+
def __init__(self):
9+
pass
10+
11+
def set_env_vars_based_on_clp(self):
12+
raise NotImplementedError()
13+
14+
@staticmethod
15+
def does_attribute_match(clp_res_model, clp_attribute, attr_name_to_check) -> bool:
16+
if f"{clp_res_model}.{clp_attribute.Name}" == attr_name_to_check or clp_attribute.Name == attr_name_to_check:
17+
return True
18+
return False
19+
20+
21+
class AWSCloudProviderEnvVarHandler(BaseCloudProviderEnvVarHandler):
22+
def __init__(self, clp_res_model: str, clp_resource_attributes: list,
23+
shell_helper: ShellHelperObject):
24+
BaseCloudProviderEnvVarHandler.__init__(self)
25+
self._clp_res_model = clp_res_model
26+
self._clp_resource_attributes = clp_resource_attributes
27+
self._shell_helper = shell_helper
28+
29+
def set_env_vars_based_on_clp(self):
30+
dec_access_key = ""
31+
dec_secret_key = ""
32+
region_flag = False
33+
34+
for attr in self._clp_resource_attributes:
35+
if self.does_attribute_match(self._clp_res_model, attr, "AWS Access Key ID"):
36+
dec_access_key = self._shell_helper.api.DecryptPassword(attr.Value).Value
37+
if self.does_attribute_match(self._clp_res_model, attr, "AWS Secret Access Key"):
38+
dec_secret_key = self._shell_helper.api.DecryptPassword(attr.Value).Value
39+
if self.does_attribute_match(self._clp_res_model, attr, "Region"):
40+
os.environ["AWS_DEFAULT_REGION"] = attr.Value
41+
region_flag = True
42+
if not region_flag:
43+
raise ValueError("Region was not found on AWS Cloud Provider")
44+
45+
# We must check both keys exist...if not then the EC2 Execution Server profile would be used (Role)
46+
if dec_access_key and dec_secret_key:
47+
os.environ["AWS_ACCESS_KEY_ID"] = dec_access_key
48+
os.environ["AWS_SECRET_ACCESS_KEY"] = dec_secret_key
49+
50+
51+
class AzureCloudProviderEnvVarHandler(BaseCloudProviderEnvVarHandler):
52+
def __init__(self, clp_res_model, clp_resource_attributes, shell_helper):
53+
BaseCloudProviderEnvVarHandler.__init__(self)
54+
self._clp_res_model = clp_res_model
55+
self._clp_resource_attributes = clp_resource_attributes
56+
self._shell_helper = shell_helper
57+
58+
def set_env_vars_based_on_clp(self):
59+
for attr in self._clp_resource_attributes:
60+
if self.does_attribute_match(self._clp_res_model, attr, self._shell_helper, "Azure Subscription ID"):
61+
os.environ["ARM_SUBSCRIPTION_ID"] = attr.Value
62+
if self.does_attribute_match(self._clp_res_model, attr, self._shell_helper, "Azure Tenant ID"):
63+
os.environ["Azure Tenant ID"] = attr.Value
64+
if self.does_attribute_match(self._clp_res_model, attr, self._shell_helper, "Azure Application ID"):
65+
os.environ["ARM_CLIENT_ID"] = attr.Value
66+
if self.does_attribute_match(self._clp_res_model, attr, self._shell_helper, "Azure Application Key", True):
67+
os.environ["ARM_CLIENT_SECRET"] = self._shell_helper.api.DecryptPassword(attr.Value).Value
Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
import os
21
from logging import Logger
32

4-
from cloudshell.iac.terraform.constants import AZURE2G_MODEL, ATTRIBUTE_NAMES
3+
from cloudshell.api.cloudshell_api import ResourceInfo
4+
5+
from cloudshell.iac.terraform.constants import AZURE2G_MODEL, ATTRIBUTE_NAMES, AWS2G_MODEL, CLP_PROVIDER_MODELS, \
6+
AWS1G_MODEL, AZURE1G_MODEL
57
from cloudshell.iac.terraform.models.shell_helper import ShellHelperObject
8+
from cloudshell.iac.terraform.services.clp_envvar_handler import AWSCloudProviderEnvVarHandler, \
9+
AzureCloudProviderEnvVarHandler
610

711

812
class ProviderHandler(object):
913
def __init__(self, logger: Logger):
1014
self.logger = logger
1115

12-
@staticmethod
13-
def initialize_provider(shell_helper: ShellHelperObject):
16+
def initialize_provider(self, shell_helper: ShellHelperObject):
1417
clp_resource_name = shell_helper.attr_handler.get_attribute(ATTRIBUTE_NAMES.CLOUD_PROVIDER)
1518
if not clp_resource_name:
1619
return
@@ -23,17 +26,8 @@ def initialize_provider(shell_helper: ShellHelperObject):
2326
raise ValueError(f"{clpr_res_fam} currently not supported")
2427

2528
try:
26-
if clp_res_model == 'Microsoft Azure' or clp_res_model == AZURE2G_MODEL:
27-
shell_helper.sandbox_messages.write_message("initializing provider...")
28-
shell_helper.logger.info("Initializing Environment variables with CloudProvider details")
29-
clp_resource_attributes = clp_details.ResourceAttributes
30-
31-
azure_attr_name_prefix = ""
32-
if clp_res_model == AZURE2G_MODEL:
33-
azure_attr_name_prefix = AZURE2G_MODEL + "."
34-
35-
ProviderHandler._set_azure_env_vars_based_on_clp(azure_attr_name_prefix, clp_resource_attributes,
36-
shell_helper)
29+
if clp_res_model in CLP_PROVIDER_MODELS:
30+
self._set_cloud_env_vars(clp_details, clp_res_model, shell_helper)
3731
else:
3832
shell_helper.logger.error(f"{clp_res_model} currently not supported")
3933
raise ValueError(f"{clp_res_model} currently not supported")
@@ -42,15 +36,25 @@ def initialize_provider(shell_helper: ShellHelperObject):
4236
shell_helper.logger.error(f"Error Setting environment variables -> {str(e)}")
4337
raise
4438

45-
@staticmethod
46-
def _set_azure_env_vars_based_on_clp(azure_attr_name_prefix, clp_resource_attributes, shell_helper):
47-
for attr in clp_resource_attributes:
48-
if attr.Name == azure_attr_name_prefix + "Azure Subscription ID":
49-
os.environ["ARM_SUBSCRIPTION_ID"] = attr.Value
50-
if attr.Name == azure_attr_name_prefix + "Azure Tenant ID":
51-
os.environ["ARM_TENANT_ID"] = attr.Value
52-
if attr.Name == azure_attr_name_prefix + "Azure Application ID":
53-
os.environ["ARM_CLIENT_ID"] = attr.Value
54-
if attr.Name == azure_attr_name_prefix + "Azure Application Key":
55-
dec_client_secret = shell_helper.api.DecryptPassword(attr.Value).Value
56-
os.environ["ARM_CLIENT_SECRET"] = dec_client_secret
39+
def _set_cloud_env_vars(
40+
self,
41+
clp_details: ResourceInfo,
42+
clp_res_model: str,
43+
shell_helper: ShellHelperObject,
44+
):
45+
shell_helper.sandbox_messages.write_message("initializing provider...")
46+
shell_helper.logger.info("Initializing Environment variables with CloudProvider details")
47+
clp_resource_attributes = clp_details.ResourceAttributes
48+
clp_handler = None
49+
50+
if clp_res_model in [AWS1G_MODEL, AWS2G_MODEL]:
51+
clp_handler = AWSCloudProviderEnvVarHandler(clp_res_model, clp_resource_attributes, shell_helper)
52+
53+
elif clp_res_model in [AZURE1G_MODEL, AZURE2G_MODEL]:
54+
clp_handler = AzureCloudProviderEnvVarHandler(clp_res_model, clp_resource_attributes, shell_helper)
55+
56+
if clp_handler:
57+
clp_handler.set_env_vars_based_on_clp()
58+
else:
59+
self.logger.error(f"Was not able to initialize provider as {clp_res_model} is not a supported model")
60+
raise ValueError(f"Was not able to initialize provider as {clp_res_model} is not a supported model")

package/cloudshell/iac/terraform/terraform_shell.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(self, driver_context: ResourceCommandContext,
2020
self._tf_service = ObjectFactory.create_tf_service(driver_context)
2121
self._logger = logger
2222
self._config = config or TerraformShellConfig()
23+
self._provider_handler = ProviderHandler(self._logger)
2324

2425
def execute_terraform(self):
2526
# initialize a logger if logger wasn't passed during init
@@ -36,7 +37,7 @@ def _execute_procedure(self, sandbox_data_handler: SandboxDataHandler, shell_hel
3637
tf_proc_executer = ObjectFactory.create_tf_proc_executer(self._config, sandbox_data_handler,
3738
shell_helper, tf_working_dir)
3839
if tf_proc_executer.can_execute_run():
39-
ProviderHandler.initialize_provider(shell_helper)
40+
self._provider_handler.initialize_provider(shell_helper)
4041
tf_proc_executer.init_terraform()
4142
tf_proc_executer.tag_terraform()
4243
tf_proc_executer.plan_terraform()
@@ -67,7 +68,7 @@ def _destroy_procedure(self, sandbox_data_handler: SandboxDataHandler, shell_hel
6768
self._handle_error_output(shell_helper, "Destroy failed due to missing local directory")
6869

6970
try:
70-
ProviderHandler.initialize_provider(shell_helper)
71+
self._provider_handler.initialize_provider(shell_helper)
7172
tf_proc_executer = ObjectFactory.create_tf_proc_executer(self._config, sandbox_data_handler,
7273
shell_helper, tf_working_dir)
7374
if tf_proc_executer.can_destroy_run():

package/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ cloudshell-shell-core==5.0.5
22
requests==2.25.1
33
cloudshell-automation-api==2021.1.0.181140
44
retry==0.9.2
5-
python-hcl2==2.0.1
5+
python-hcl2==2.0.1

package/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.0
1+
1.1.0
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
env/
12+
build/
13+
develop-eggs/
14+
dist/
15+
downloads/
16+
eggs/
17+
.eggs/
18+
lib/
19+
lib64/
20+
parts/
21+
sdist/
22+
var/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
27+
# PyInstaller
28+
# Usually these files are written by a python script from a template
29+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
30+
*.manifest
31+
*.spec
32+
33+
# Installer logs
34+
pip-log.txt
35+
pip-delete-this-directory.txt
36+
37+
# Unit test / coverage reports
38+
htmlcov/
39+
.tox/
40+
.coverage
41+
.coverage.*
42+
.cache
43+
nosetests.xml
44+
coverage.xml
45+
*,cover
46+
.hypothesis/
47+
48+
# Translations
49+
*.mo
50+
*.pot
51+
52+
# Django stuff:
53+
*.log
54+
55+
# Sphinx documentation
56+
docs/_build/
57+
58+
# PyBuilder
59+
target/
60+
cloudshell_config.yml

0 commit comments

Comments
 (0)