Description: AWS EC2 instance to run CT existing account onboarding (fdp-1qj64b38g) Metadata: 'AWS::CloudFormation::Interface': ParameterGroups: - Label: default: Network Configuration Parameters: - VPC - RemoteAccessCIDR - Label: default: Linux Instance Configuration Parameters: - KeyPair - LinuxSubnet - LinuxInstanceType - LatestAmiId ParameterLabels: VPC: default: VPC KeyPair: default: Key Pair RemoteAccessCIDR: default: Remote Access CIDR Block LinuxSubnet: default: Linux Subnet LatestAmiId: default: SSM key to the latest Amazon linux AMI Parameters: VPC: Type: 'AWS::EC2::VPC::Id' Description: Select the VPC where the EC2 instances will be created ConstraintDescription: must be an existing VPC LinuxSubnet: Type: 'AWS::EC2::Subnet::Id' Description: Select subnet for Linux Instance ConstraintDescription: must be an existing subnet KeyPair: Description: Name of existing EC2 key pair for Linux Instances Type: 'AWS::EC2::KeyPair::KeyName' RemoteAccessCIDR: Description: CIDR block to allow access to linux instances Type: String AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})' ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. LatestAmiId: Type: 'AWS::SSM::Parameter::Value' Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Resources: SSHSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: VpcId: !Ref VPC GroupDescription: Enable ssh access SecurityGroupIngress: - Description: allow icmp IpProtocol: icmp FromPort: '-1' ToPort: '-1' CidrIp: !Ref RemoteAccessCIDR - Description: allow SSH IpProtocol: tcp FromPort: '22' ToPort: '22' CidrIp: !Ref RemoteAccessCIDR ScriptRunnerInstance: Type: 'AWS::EC2::Instance' Metadata: 'AWS::CloudFormation::Init': configSets: All: - prereqs - downloadscript prereqs: packages: yum: python3: [] commands: 1_install_boto3: command: sudo pip3 install boto3 downloadscript: commands: 1_get_enroll_script: command: >- wget https://raw.githubusercontent.com/aws-samples/aws-control-tower-reference-architectures/master/customizations/AccountFactory/BatchUpdate/update_pp.py -O update_pp.py cwd: /home/ec2-user ignoreErrors: 'true' 2_chmod_enroll_script: command: chmod +x update_pp.py cwd: /home/ec2-user ignoreErrors: 'true' 3_set_region: command: !Sub 'echo export AWS_DEFAULT_REGION=${AWS::Region} >> .bash_profile' cwd: /home/ec2-user ignoreErrors: 'true' Properties: InstanceType: t3.micro IamInstanceProfile: !Ref CTInstanceProfile SubnetId: !Ref LinuxSubnet ImageId: !Ref LatestAmiId KeyName: !Ref KeyPair Tags: - Key: Name Value: !Sub 'CT-UpdateRunner-${AWS::StackName}' SecurityGroupIds: - !GetAtt - SSHSecurityGroup - GroupId UserData: !Base64 'Fn::Join': - '' - - | #!/bin/bash -xe - | yum update -y aws-cfn-bootstrap - | # Install the files and packages from the metadata - '/opt/aws/bin/cfn-init -v ' - ' --stack ' - !Ref 'AWS::StackName' - ' --resource ScriptRunnerInstance ' - ' --configsets All ' - ' --region ' - !Ref 'AWS::Region' - |+ - | # Signal the status from cfn-init - '/opt/aws/bin/cfn-signal -e $? ' - ' --stack ' - !Ref 'AWS::StackName' - ' --resource ScriptRunnerInstance ' - ' --region ' - !Ref 'AWS::Region' - |+ CreationPolicy: ResourceSignal: Timeout: PT5M CTUpdateInstanceProfileRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: account-update-inline-policy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'sso:GetProfile' - 'sso:CreateProfile' - 'sso:UpdateProfile' - 'sso:GetSSOStatus' - 'sso:GetTrust' - 'sso:CreateTrust' - 'sso:UpdateTrust' - 'sso:GetApplicationInstance' - 'sso:ListDirectoryAssociations' - 'sso:ListPermissionSets' - 'sso:AssociateProfile' - 'sso:GetPermissionSet' - 'sso:CreateApplicationInstance' - 'sso:ProvisionApplicationInstanceForAWSAccount' - 'sso:ProvisionApplicationProfileForAWSAccountInstance' - 'sso:ProvisionSAMLProvider' - 'sso:ListProfileAssociations' - 'sso-directory:ListMembersInGroup' - 'sso-directory:SearchGroups' - 'sso-directory:SearchGroupsWithGroupName' - 'sso-directory:SearchUsers' - 'sso-directory:CreateUser' - 'sso-directory:DescribeGroups' - 'sso-directory:DescribeDirectory' - 'sso-directory:GetUserPoolInfo' - 'controltower:CreateManagedAccount' - 'controltower:DescribeManagedAccount' - 'controltower:DeregisterManagedAccount' - 's3:GetObject' - 'organizations:describeOrganization' - 'organizations:TagResource' - 'organizations:UntagResource' - 'sso:DescribeRegisteredRegions' - 'servicecatalog:DisassociatePrincipalFromPortfolio' - 'servicecatalog:AssociatePrincipalWithPortfolio' Resource: '*' ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSOrganizationsReadOnlyAccess - arn:aws:iam::aws:policy/AWSServiceCatalogAdminFullAccess CTInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Path: "/" Roles: - Ref: "CTUpdateInstanceProfileRole" GetPortfolioIdLambda: Type: 'AWS::Lambda::Function' DependsOn: IamRoleLambdaForSC Properties: Code: ZipFile: | import logging import boto3 import cfnresponse from botocore.exceptions import ClientError LOGGER = logging.getLogger() LOGGER.setLevel(logging.INFO) SC = boto3.client('servicecatalog') def list_portfolios(): '''List all portfolios in the account''' porfolio_list = list() try: sc_paginator = SC.get_paginator('list_portfolios') sc_page_iterator = sc_paginator.paginate() except ClientError as exe: LOGGER.error('Unable to get portfolio list: %s', str(exe)) for page in sc_page_iterator: porfolio_list += page['PortfolioDetails'] return porfolio_list def get_portfolio_id(port_name): '''Return portfolio Id for a given portfolio name''' port_id = None port_list = list_portfolios() for item in port_list: if item['DisplayName'] == port_name: port_id = item['Id'] return port_id def handler(event, context): '''Get portfolio Id and retuen the calie''' port_name = 'AWS Control Tower Account Factory Portfolio' port_id = get_portfolio_id(port_name) result = {} result['Data'] = port_id cfnresponse.send(event, context, cfnresponse.SUCCESS, result, port_id) Handler: index.handler Runtime: python3.7 MemorySize: 128 Role: !GetAtt IamRoleLambdaForSC.Arn Timeout: 60 IamRoleLambdaForSC: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSServiceCatalogAdminFullAccess - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole GetPortfolioId: Type: 'Custom::TriggerLambda' Properties: ServiceToken: !GetAtt GetPortfolioIdLambda.Arn AssociateGroupToPortfolio: Type: AWS::ServiceCatalog::PortfolioPrincipalAssociation Properties: AcceptLanguage: en PortfolioId: !Ref GetPortfolioId PrincipalARN: !GetAtt CTUpdateInstanceProfileRole.Arn PrincipalType: IAM Outputs: SSHAddress: Description: DNS URL of the instance Value: !GetAtt - ScriptRunnerInstance - PublicDnsName