AWSTemplateFormatVersion: 2010-09-09 Description: Baseline Resources for Service Catalog - 01/09/21 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Share Configuration Parameters: - OrganizationalUnit - Version - Label: default: Portfolio Configuration Parameters: - PortfolioDisplayName - PortfolioDescription - PortfolioProviderName - Label: default: Product Configuration Parameters: - ProductName - ProductDescription - ProductOwner - Label: default: Provisioning Template Configuration Parameters: - ProvisioningName - ProvisioningDescription - ProvisioningTemplate Parameters: OrganizationalUnit: Type: String Description: Organization Id to share the portfolio with Version: Type: String Description: Parameter to trigger the custom resource update. Please change this value every time you want to trigger the update logic of custom resource Default: 1 PortfolioDisplayName: Type: String Description: Portfolio display name Default: "ChaosEngineering" PortfolioDescription: Type: String Description: Description of portfolio Default: "Desc" PortfolioProviderName: Type: String Description: Name of portfolio provider Default: "AWSPlatformTeam" ProductName: Type: String Description: Name of product Default: "ChaosProduct" ProductDescription: Type: String Description: Description of product Default: "Desc" ProductOwner: Type: String Description: Name of product owner Default: "PlatformTeam" ProvisioningName: Type: String Description: Name of provisioned product association Default: "FISExperiment" ProvisioningDescription: Type: String Description: Description of provisioned product association Default: "Desc" ProvisioningTemplate: Type: String Description: S3 URL for product CloudFormation template #Chaos Engineering Service Catalog Portfolio Resources: ChaosEngineeringPortfolio: Type: "AWS::ServiceCatalog::Portfolio" Properties: DisplayName: !Sub '${PortfolioDisplayName}' AcceptLanguage: "en" Description: !Sub '${PortfolioDescription}' ProviderName: !Sub '${PortfolioProviderName}' #FIS Service Catalog Product ChaosProduct: Type: "AWS::ServiceCatalog::CloudFormationProduct" Properties: Name: !Ref ProductName Description: !Ref ProductDescription Owner: "AWSPlatformTeam" ProvisioningArtifactParameters: - Name: !Ref ProvisioningName Description: !Ref ProvisioningDescription Info: LoadTemplateFromURL: !Ref ProvisioningTemplate #Association between products and portfolio ExperimentAssociation: Type: "AWS::ServiceCatalog::PortfolioProductAssociation" Properties: ProductId: !Ref ChaosProduct PortfolioId: !Ref ChaosEngineeringPortfolio #Lambda Execution Role LambdaExecutionRole: Type: AWS::IAM::Role Properties: Description: Iam Role for Lambda Execution Path: '/' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Sid: '' Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSLambda_FullAccess - arn:aws:iam::aws:policy/AWSServiceCatalogAdminFullAccess - arn:aws:iam::aws:policy/AWSOrganizationsFullAccess - arn:aws:iam::aws:policy/CloudWatchFullAccess RoleName: Executionroleportfolioshare # Lambda Function PortfolioShare PortfolioShareLambda: Type: AWS::Lambda::Function Properties: Description: Lambda Function to share the Service Catalogue Portfolio FunctionName: PortfolioShareLambda Handler: "index.lambda_handler" MemorySize: 128 Timeout: 120 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Runtime: python3.8 Environment: Variables: PortfolioId: Ref: ChaosEngineeringPortfolio OrganizationalUnit: Ref: OrganizationalUnit Code: ZipFile: | import json import boto3 import logging, sys, traceback import urllib3 import os from os import path http = urllib3.PoolManager() from botocore.exceptions import ClientError from urllib.parse import unquote # Setting base-config for Logging logger = logging.getLogger() logger.setLevel(logging.INFO) SUCCESS = "SUCCESS" FAILED = "FAILED" def sendResponse(event, context, responseStatus, responseData, reason=None, physical_resource_id=None): responseBody = {'Status': responseStatus, 'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name, 'PhysicalResourceId': physical_resource_id or context.log_stream_name, 'StackId': event['StackId'], 'RequestId': event['RequestId'], 'LogicalResourceId': event['LogicalResourceId'], 'Data': responseData} print ('RESPONSE BODY:n' + json.dumps(responseBody)) responseUrl = event['ResponseURL'] json_responseBody = json.dumps(responseBody) headers = { 'content-type' : '', 'content-length' : str(len(json_responseBody)) } try: response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody) #response = response.send(responseUrl, data=json_responseBody, headers=headers) print ("Status code: " +response.reason) except Exception as e: print ("send(..) failed executing requests.put(..): " + str(e)) def lambda_handler(event,context): client = boto3.client('servicecatalog') portfolio_id = (os.environ['PortfolioId']) organizational_unit= (os.environ['OrganizationalUnit']) try: if event['RequestType'] == 'Delete': logger.info("Received a Delete Request...") responseStatus = "SUCCESS" responseData = {} elif event['RequestType'] == 'Create': logger.info("Received a Create Request...") responseData = {} responseStatus = "SUCCESS" response = client.create_portfolio_share( AcceptLanguage='en', PortfolioId=portfolio_id, OrganizationNode={ 'Type': 'ORGANIZATION', 'Value': organizational_unit }, ShareTagOptions=True ) elif event['RequestType'] == 'Update': logger.info("Received a Update Request...") responseData = {} responseStatus = "SUCCESS" response = client.create_portfolio_share( AcceptLanguage='en', PortfolioId=portfolio_id, OrganizationNode={ 'Type': 'ORGANIZATION', 'Value': organizational_unit }, ShareTagOptions=True ) except Exception as exp: responseStatus = "FAILED" responseData = {} exception_type, exception_value, exception_traceback = sys.exc_info() traceback_string = traceback.format_exception(exception_type, exception_value, exception_traceback) err_msg = json.dumps({ "errorType": exception_type.__name__, "errorMessage": str(exception_value), "stackTrace": traceback_string }) logger.error(err_msg) sendResponse(event, context, responseStatus, responseData) #Lambda Trigger PortfolioCustomResource: Type: AWS::CloudFormation::CustomResource DependsOn: PortfolioShareLambda Properties: ServiceToken: !GetAtt PortfolioShareLambda.Arn DummyVersion: !Ref Version Outputs: ChaosEngineeringPortfolio: Value: !Ref ChaosEngineeringPortfolio PortfolioShareLambda: Description: Lambda Function to Share the Service Catalogue Portfolio Value: !GetAtt PortfolioShareLambda.Arn