Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(deployment): FSS Deployment tool for Azure #89

Draft
wants to merge 51 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
aeff3f8
Updating .gitignore for macOS and VSCode files
GeorgeDavis-TM Jul 26, 2022
5d5740a
Initial draft
GeorgeDavis-TM Jul 26, 2022
9ae6d7e
Initial draft
GeorgeDavis-TM Jul 26, 2022
26f4d6d
Updating requirements.txt
GeorgeDavis-TM Jul 27, 2022
dd28b88
Updating package name and description
GeorgeDavis-TM Jul 27, 2022
6c8e5ff
Finalizing plugin code
GeorgeDavis-TM Jul 27, 2022
a9fcf0f
Using custom stage variables to aggregate config params, package incl…
GeorgeDavis-TM Jul 27, 2022
1ff37e1
Adding more macOS temporary files to .gitignore list
GeorgeDavis-TM Jul 27, 2022
e9fadd4
Merge pull request #1 from GeorgeDavis-TM/gcp-post-scan-slack-notific…
GeorgeDavis-TM Jul 27, 2022
2a31c2c
Merge branch 'master' of https://github.com/GeorgeDavis-TM/cloudone-f…
GeorgeDavis-TM Jul 27, 2022
1a8ca83
Updating requirements.txt
GeorgeDavis-TM Jul 27, 2022
41f6ce2
Updated README
GeorgeDavis-TM Jul 28, 2022
2396ad5
Adding npm package info
GeorgeDavis-TM Jul 28, 2022
6abc249
Adding .gitignore
GeorgeDavis-TM Jul 28, 2022
c144cec
Finalizing plugin code
GeorgeDavis-TM Jul 28, 2022
30b04ef
Updating serverless.yaml with custom stage variables
GeorgeDavis-TM Jul 28, 2022
907657d
Code cleanup, raising exception for non-200 HTTP responses
GeorgeDavis-TM Jul 28, 2022
d180173
Using a default Slack URL, removing comments
GeorgeDavis-TM Jul 28, 2022
137a61c
Bumping version code to 0.1.1
GeorgeDavis-TM Jul 28, 2022
027acc9
Code cleanup, raising exception for non-200 HTTP responses
GeorgeDavis-TM Jul 28, 2022
5965c44
Using a default Slack URL, removing comments
GeorgeDavis-TM Jul 28, 2022
a513f08
Merge branch 'gcp-post-scan-slack-notification' of https://github.com…
GeorgeDavis-TM Jul 28, 2022
d2b2129
Updated README.md
GeorgeDavis-TM Jul 28, 2022
a2e6caf
Bumping version code to 0.1.1
GeorgeDavis-TM Jul 28, 2022
23871fe
Updated README.md
GeorgeDavis-TM Jul 28, 2022
b9fe716
Bumping version code to 0.1.1
GeorgeDavis-TM Jul 28, 2022
3b581b2
Merge pull request #2 from GeorgeDavis-TM/gcp-post-scan-slack-notific…
GeorgeDavis-TM Jul 28, 2022
9773020
Merge pull request #3 from GeorgeDavis-TM/gcp-post-scan-teams-notific…
GeorgeDavis-TM Jul 28, 2022
f21b08c
fix:Update post-scan-actions/gcp-python-slack-notification/README.md
GeorgeDavis-TM Jul 28, 2022
156053e
fix:Update post-scan-actions/gcp-python-slack-notification/README.md
GeorgeDavis-TM Jul 28, 2022
71702cc
fix:Update post-scan-actions/gcp-python-slack-notification/serverless…
GeorgeDavis-TM Jul 28, 2022
cf3f7d1
fix: Update post-scan-actions/gcp-python-slack-notification/package.json
GeorgeDavis-TM Jul 28, 2022
4525f58
Merge branch 'gcp-post-scan-slack-notification' of https://github.com…
GeorgeDavis-TM Jul 28, 2022
febfa28
fix: adding serverless as a dev dependency, ignoring node_modules dur…
GeorgeDavis-TM Jul 28, 2022
f49c83b
fix: switching to param to enable CLI and Stage parameter options
GeorgeDavis-TM Jul 28, 2022
cc4f412
fix: Switching from custom.stages to params, such that CLI and Stage …
GeorgeDavis-TM Jul 28, 2022
c44beee
docs: Updating docs
GeorgeDavis-TM Jul 28, 2022
cbb7b3e
docs: Updated package.json
GeorgeDavis-TM Jul 28, 2022
193b2f0
docs: Update
GeorgeDavis-TM Jul 28, 2022
97746c9
Merge branch 'master' of https://github.com/GeorgeDavis-TM/cloudone-f…
GeorgeDavis-TM Jul 28, 2022
1e5e1d7
Merge pull request #4 from GeorgeDavis-TM/gcp-post-scan-slack-notific…
GeorgeDavis-TM Jul 28, 2022
5776d71
docs: Updated documentation with deployment steps
GeorgeDavis-TM Jul 28, 2022
367a608
ci: Adding dependency information and deployment params
GeorgeDavis-TM Jul 28, 2022
1750db1
style: Switched from double quotes to single quotes to reflect Dictio…
GeorgeDavis-TM Jul 28, 2022
a0c00c3
Merge pull request #5 from GeorgeDavis-TM/gcp-post-scan-teams-notific…
GeorgeDavis-TM Jul 29, 2022
1edaf77
fix: Changing package.include and package.exclude to package.patterns
GeorgeDavis-TM Jul 29, 2022
6cf6c43
fix: Changing package.include and package.exclude to package.patterns
GeorgeDavis-TM Jul 29, 2022
7d0767c
Merge pull request #6 from GeorgeDavis-TM/gcp-post-scan-slack-notific…
GeorgeDavis-TM Jul 29, 2022
1bde583
Merge pull request #7 from GeorgeDavis-TM/gcp-post-scan-teams-notific…
GeorgeDavis-TM Jul 29, 2022
e878e68
Imported code from trendmicro/cloudone-community/tree/deployment-azur…
GeorgeDavis-TM Aug 6, 2022
9828bd8
refactor: Remove gcp-python-slack-notification and gcp-python-teams-n…
GeorgeDavis-TM Aug 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@ packaged.yaml
.terraform.lock.hcl
terraform.tfstate
terraform.tfstate.backup

# macOS
**/.dccache
**/.DS_Store

# VSCode
**/.vscode
74 changes: 74 additions & 0 deletions deployment/azure-python-deploy-to-all-existing-storage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Deploy to All Existing Azure Resource
This script will deploy File Storage Security Stack to all storage accounts unless defined in `exclude.txt` text file. After deployment, the stacks will be registered with the Cloud One Console.

**Before you deploy**

* Obtain your Cloud One API Key
- Generate API Key: [Cloud One API Key](https://cloudone.trendmicro.com/docs/account-and-user-management/c1-api-key/)

<hr>

**1. Clone Repo**
- Clone this repository `git clone https://github.com/trendmicro/cloudone-community.git`
- After cloning repo:
```
cd .\cloudone-community\File-Storage-Security/Deployment/azure-python-deploy-to-all-existing-Azure
```

**2. Configure the Exclusions text file `exclude.txt`**
* Create a new file called `exclude.txt` with names of Azure storage accounts to exclude from FSS deployment.
- 1 per line, Example: [exclude.txt](https://github.com/trendmicro/cloudone-community/blob/main/File-Storage-Security/Deployment/python-deploy-to-all-existing/exclude.txt)
* For organizations with a large number of storage accounts, a list of Azure storage accounts can be piped into `exclude.txt` using `azure-cli` or `PowerShell`:
```
# Bash
az storage account list --query "[?tags.AutoDeployFSS != 'True'].name" --output tsv > exclude.txt
cat exclude.txt

# PowerShell
Clear-Content -Path exclude.txt
Get-AzStorageAccount | Where-Object {$_.tags.AutoDeployFSS -ne 'True'} | Select-Object -Property StorageAccountName | ConvertTo-JSON | Out-File -FilePath exclude.json
$json = (Get-Content "exclude.json" -Raw) | ConvertFrom-Json
foreach($v in $json.StorageAccountName) {
Write-Output "${v}" >> exclude.txt
}
more exclude.txt
```
**3. Configuration file**

* Complete the `config.json` configuration file with valid input.

| Fields | Environment Variable | Type | Description | Required? |
|--------| ---- | ----------- | --------- | --------- |
| `app_id` | | String | Azure Application ID | Yes |
| `tenant_id` | | String | Azure Tenant ID | Yes |
| `subscription_id` | AZURE_SUBSCRIPTION_ID | String | Azure Subscription ID | Yes |
| `keyvault_uri` | | String | Azure KeyVault URI | Yes |
|`cloudone.region` | CLOUDONE_REGION | String | Cloud One Region Example: us-1 or ca-1 | Yes |
| `cloudone.api_key` | CLOUDONE_API_KEY | String | Cloud One File Storage Security API Key. You can create an API Key using these instructions - https://cloudone.trendmicro.com/docs/workload-security/api-cookbook-set-up/#create-an-api-key | Yes |
| `cloudone.max_storage_stack_per_scanner_stack` | MAX_STORAGE_STACK_PER_SCANNER_STACK | Number | Recommended to set to 50, i.e, for every 50 Storage stacks, 1 Scanner stack would be created. Contact the product team for your usecase requirements. | Yes |
| `azure_creds.key` | | Boolean | Azure Credentials Client Value | Yes |
| `azure_creds.secret` | | String | Azure Credentials Secret Value | Yes |

**4. Deploy Tool via the Serverless Framework**
* Open terminal/cmd:
```
serverless deploy -s dev
```

# Additional Notes

### Tags

The Script will choose whether or not to deploy a storage stack depending on a storage accounts' tags. **See below for details**:

| Tag | Value | Behavior |
| -------------- | --------------------- |-------------------------------------------------------------- |
| [no tag] | [none] | No action |
| `AutoDeployFSS` | `True` | Storage Stack and Scanner Stack will be deployed |
| `AutoDeployFSS` | `(any other value)` | Skip |
| `FSSMonitored` | `yes` | Storage Stack Already Exists, Scanner Stack associated (skip) |
| `FSSMonitored` | `(any other value)` | Skip |

### Supported FSS regions

Please note this script deploys Scanner Stacks to select Azure locations as listed in the supported document here - [What Azure services and regions are supported?](https://cloudone.trendmicro.com/docs/file-storage-security/supported-azure/). Storage Stacks that are not present in the same Azure locations will be mapped to Scanner Stacks deployed in the same geographyGroup as defined by Azure. Any data transfer cost(s) incurred in this data transfer would be your responsibility.
30 changes: 30 additions & 0 deletions deployment/azure-python-deploy-to-all-existing-storage/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/bash

# Function app and storage account names must be unique.

# Variable block
let "randomIdentifier=$RANDOM*$RANDOM"
location="eastus"
resourceGroup="azure-functions-deployfss-rg-$randomIdentifier"
tag="function-app-deployfss"
storage="deployfss$randomIdentifier"
functionApp="deployfss-serverless-function-$randomIdentifier"
skuStorage="Standard_LRS"
functionsVersion="4"
pythonVersion="3.9" #Allowed values: 3.7, 3.8, and 3.9

# Create a resource group
echo "Creating $resourceGroup in "$location"..."
az group create --name $resourceGroup --location "$location" --tags $tag

# Create an Azure storage account in the resource group.
echo "Creating $storage"
az storage account create --name $storage --location "$location" --resource-group $resourceGroup --sku $skuStorage

# Create a serverless python function app in the resource group.
echo "Creating $functionApp"
az functionapp create --name $functionApp --storage-account $storage --consumption-plan-location "$location" --resource-group $resourceGroup --os-type Linux --runtime python --runtime-version $pythonVersion --functions-version $functionsVersion

# Publish function app
cd deployToAllExistingStorageAccounts
func azure functionapp publish $functionApp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
Modified class. Original version can be found at https://raw.githubusercontent.com/Azure-Samples/resource-manager-python-template-deployment/master/deployer.py

Modifications include
- uuid7 instead of Haikunator, for a shorter random suffix.
- using dynamic regions for deployment
"""

"""A deployer class to deploy a template on Azure"""
import os.path
import json
from azure.identity import ClientSecretCredential
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.resource.resources.models import DeploymentMode
from azure.mgmt.resource.resources.models import Deployment
from azure.mgmt.resource.resources.models import DeploymentProperties

import keyvault

class Deployer(object):
""" Initialize the deployer class with subscription, resource group and public key.

:raises IOError: If the public key path cannot be read (access or not exists)
:raises KeyError: If AZURE_CLIENT_ID, AZURE_CLIENT_SECRET or AZURE_TENANT_ID env
variables or not defined
"""

def __init__(self, subscription_id, resource_group_name):
self.subscription_id = subscription_id
self.resource_group_name = resource_group_name
# self.credentials = DefaultAzureCredential(exclude_environment_credential=False)

# TODO: Store credentials in the Azure Key Vault, instead of environment variables
# print("\nClient ID : " + str(keyvault.get_secret_from_keyvault('FSS-AUTODEPLOY-CLIENT-ID')) + "\nClient Secret : " + str(keyvault.get_secret_from_keyvault('FSS-AUTODEPLOY-CLIENT-SECRET')))

# self.credentials = ServicePrincipalCredentials(
# client_id=os.environ['AZURE_CLIENT_ID'],
# secret=os.environ['AZURE_CLIENT_SECRET'],
# tenant=os.environ['AZURE_TENANT_ID']
# )
self.credentials = ClientSecretCredential(
client_id=os.environ['AZURE_CLIENT_ID'],
client_secret=os.environ['AZURE_CLIENT_SECRET'],
tenant_id=os.environ['AZURE_TENANT_ID']
)
self.client = ResourceManagementClient(self.credentials, self.subscription_id)

def deploy(self, azure_location, stack_type, stack_params={}):
"""Deploy the template to a resource group."""
self.client.resource_groups.create_or_update(
self.resource_group_name,
{
'location': azure_location
}
)

template_file_name = None
if stack_type == "scanner":
template_file_name = "FSS-Scanner-Stack-Template.json"
elif stack_type == "storage":
template_file_name = "FSS-Storage-Stack-Template.json"

template_path = os.path.join(os.path.dirname(__file__), 'templates', template_file_name)
with open(template_path, 'r') as template_file_fd:
template = json.load(template_file_fd)

parameters = {}
if stack_params:
parameters.update(stack_params)
parameters = {k: {'value': v} for k, v in parameters.items()}

deployment_properties = DeploymentProperties(
mode = DeploymentMode.incremental,
template = template,
parameters = parameters
)

# TODO: Tag your deployments so you can keep track
deployment_async_operation = self.client.deployments.begin_create_or_update(
resource_group_name = self.resource_group_name,
deployment_name = self.resource_group_name + '-dep',
parameters = Deployment(properties = deployment_properties)
)
deployment_async_operation.wait()

deployment_outputs = self.client.deployments.get(
resource_group_name = self.resource_group_name,
deployment_name = self.resource_group_name + '-dep'
)

return deployment_outputs.properties.outputs

def destroy(self):
"""Destroy the given resource group"""
self.client.resource_groups.delete(self.resource_group_name)
Loading